Erreur 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...
                           ^

Dans une requête groupée, chaque colonne sélectionnée doit soit faire partie de la clé de regroupement, soit être enveloppée dans un agrégat — sinon il y a de nombreuses valeurs candidates par groupe et aucune règle pour en choisir une. Les bases qui en choisissent une « obligeamment » quand même (comme le faisait l'ancien MySQL) renvoient des données non déterministes ; PostgreSQL refuse.

Ce que signifie cette erreur

Chaque ligne de résultat d'une requête GROUP BY représente de nombreuses lignes sources. department convient (il définit le groupe), count(*) convient (il agrège le groupe) — mais name ? Lequel des 40 employés du service devrait le fournir ? La requête est réellement ambiguë, et l'erreur est la réponse du standard SQL.

Une exception documentée : groupez par la clé primaire d'une table, et vous pouvez sélectionner n'importe quelle colonne de cette table — elles dépendent fonctionnellement de la clé, il n'y a donc pas d'ambiguïté. GROUP BY u.id autorise légitimement SELECT u.name, u.email, ....

Causes fréquentes

Comment la diagnostiquer

Pour la colonne signalée, répondez à une question : la valeur de quelle ligne veux-je réellement ? « N'importe laquelle, elles sont toutes identiques » → groupez par la clé qui rend cela vrai (ou la PK). « Le max/min/somme » → agrégez. « Celle de la ligne la plus récente » → vous voulez DISTINCT ON, pas GROUP BY.

Comment la corriger

-- La valeur est déterminée par le groupe → groupez par la clé :
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 admis

-- Vous en voulez un agrégat :
SELECT department, count(*), max(salary) FROM employees GROUP BY department;

-- Vous voulez « la ligne la plus récente par groupe » → DISTINCT ON :
SELECT DISTINCT ON (customer_id) *
FROM orders
ORDER BY customer_id, created_at DESC;

PostgreSQL 16 ajoute aussi any_value() pour le vrai cas « n'importe laquelle fera l'affaire ».

🔍 La même ambiguïté sous un autre costume : more than one row returned by a subquery.