Error de PostgreSQL: column must appear in the GROUP BY clause

ERROR:  column "employees.name" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT department, name, count(*) FROM employees GROUP BY d...
                           ^

En una consulta agrupada, cada columna seleccionada debe ser o parte de la clave de agrupación o estar envuelta en un agregado — de lo contrario hay muchos valores candidatos por grupo y ninguna regla para elegir uno. Las bases de datos que "servicialmente" eligen uno de todos modos (como hacía el MySQL antiguo) devuelven datos no deterministas; PostgreSQL se niega.

Qué significa este error

Cada fila de resultado de una consulta GROUP BY representa muchas filas de origen. department está bien (define el grupo), count(*) está bien (agrega el grupo) — pero ¿name? ¿Cuál de los 40 empleados del departamento debería aportarlo? La consulta es genuinamente ambigua, y el error es la respuesta del estándar SQL.

Una excepción documentada: agrupa por la clave primaria de una tabla, y puedes seleccionar cualquier columna de esa tabla — son funcionalmente dependientes de la clave, así que no hay ambigüedad. GROUP BY u.id permite legítimamente SELECT u.name, u.email, ....

Causas comunes

Cómo diagnosticarlo

Para la columna señalada, responde una pregunta: ¿el valor de qué fila quiero realmente? "Cualquiera, son todos iguales" → agrupa por la clave que lo hace así (o la PK). "El máx/mín/suma" → agrega. "El de la fila más reciente" → quieres DISTINCT ON, no GROUP BY.

Cómo solucionarlo

-- El valor está determinado por el grupo → agrupa por la clave:
SELECT u.id, u.name, count(o.id)
FROM users u JOIN orders o ON o.user_id = u.id
GROUP BY u.id;                      -- PK: u.name está permitido

-- Quieres un agregado de él:
SELECT department, count(*), max(salary) FROM employees GROUP BY department;

-- Quieres "la fila más reciente por grupo" → DISTINCT ON:
SELECT DISTINCT ON (customer_id) *
FROM orders
ORDER BY customer_id, created_at DESC;

PostgreSQL 16 también añade any_value() para el caso genuino de "cualquiera vale".

🔍 La misma ambigüedad con otro disfraz: more than one row returned by a subquery.