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

In una query raggruppata, ogni colonna selezionata deve essere o parte della chiave di raggruppamento o avvolta in un aggregato — altrimenti ci sono molti valori candidati per gruppo e nessuna regola per sceglierne uno. I database che "premurosamente" ne scelgono uno comunque (come faceva MySQL più vecchio) restituiscono dati non deterministici; PostgreSQL rifiuta.

Cosa significa questo errore

Ogni riga di risultato di una query GROUP BY rappresenta molte righe sorgente. department va bene (definisce il gruppo), count(*) va bene (aggrega il gruppo) — ma name? Quale dei 40 dipendenti del reparto dovrebbe fornirlo? La query è genuinamente ambigua, e l'errore è la risposta dello standard SQL.

Un'eccezione documentata: raggruppa per la primary key di una tabella, e puoi selezionare qualsiasi colonna di quella tabella — sono funzionalmente dipendenti dalla chiave, quindi non c'è ambiguità. GROUP BY u.id consente legittimamente SELECT u.name, u.email, ....

Cause comuni

Come diagnosticarlo

Per la colonna segnalata, rispondi a una domanda: quale valore di riga voglio davvero? "Uno qualsiasi, sono tutti uguali" → raggruppa per la chiave che lo rende tale (o la PK). "Il max/min/sum" → aggrega. "Quello della riga più recente" → vuoi DISTINCT ON, non GROUP BY.

Come risolverlo

-- Il valore è determinato dal gruppo → raggruppa per la chiave:
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 è ammesso

-- Ne vuoi un aggregato:
SELECT department, count(*), max(salary) FROM employees GROUP BY department;

-- Vuoi "la riga più recente per gruppo" → DISTINCT ON:
SELECT DISTINCT ON (customer_id) *
FROM orders
ORDER BY customer_id, created_at DESC;

PostgreSQL 16 aggiunge anche any_value() per il caso genuino "uno qualsiasi va bene".

🔍 La stessa ambiguità in un altro costume: more than one row returned by a subquery.