Skip to content
SQLSimplified

Why SQL Says a Column Must Appear in the GROUP BY Clause

Fix the GROUP BY "column must appear" error, understand why it fires, and see why MySQL's silent version of the same bug is worse than an error.

GROUP BYAggregationDebugging

The query that won't run

You add one column to a SELECT that already has a GROUP BY, and the database refuses to run it:

column "first_name" must appear in the GROUP BY clause or be used in an
aggregate function

Nothing about the query looks wrong. It is a column that genuinely exists, on a table you're already querying. The rule causing this: every column in SELECT must either be listed in GROUP BY, or be wrapped in an aggregate function like COUNT, SUM, or AVG. If it's neither, the database has no single value to return for it, because GROUP BY has already collapsed many rows into one.

Run the broken version yourself:

Loading playground environment...

Five departments, but the query asks for first_name per department, and a department has several employees, each with a different first name. There is no single first name to put in that cell, so the engine won't guess. That's the whole error: not a typo, not a missing join, just an unanswerable question.

The real fix: decide what "one row per group" means

The fix isn't to satisfy the parser, it's to decide what a department's row should actually contain. If you want a per-department summary, aggregate the columns that vary within the group:

Loading playground environment...

Now every column has an unambiguous value per group: department_id is the group itself, headcount and avg_salary are aggregates computed across the rows in it. That is the pattern: group by the thing you are summarizing over, aggregate everything else.

The "fix" that makes the error go away and the query useless

The error message makes people reach for the wrong fix: throw every selected column into GROUP BY until it compiles. It works, syntactically:

Loading playground environment...

Twenty rows. Compare that to a plain count of the table:

Loading playground environment...

Also twenty. GROUP BY on every selected column groups each row with itself, because the combination of department, name, and salary is already unique per employee, so nothing actually got summarized. The query stopped erroring and started lying instead: it looks like a grouped report, but it's the raw table with extra syntax. If your goal was one row per department, this "fix" didn't get you there; it just hid the fact that you hadn't decided what to aggregate.

A query that runs is not a query that's right

GROUP BY accepting your columns only proves the syntax is legal. It says nothing about whether the result still answers your original question. Check the row count against what you expected before trusting the output.

Grouping through a join

The same rule applies once a join is involved, and it's where the error catches people who thought they'd already fixed it. Say you want headcount and average salary per department, but by name instead of ID:

Loading playground environment...

d.name is grouped, e.salary is aggregated, so every selected column is accounted for, same as before, just spread across two tables. The rule doesn't care which table a column comes from, only whether it's grouped or aggregated.

To filter on the aggregate itself, say departments averaging over $100,000, WHERE will not work, because WHERE runs before the grouping exists. That's HAVING's job, and it's a big enough topic on its own; see GROUP BY with HAVING for a full walkthrough. Here it's one clause added to what you already have:

Loading playground environment...

Engineering is the only department clearing six figures on average.

The version of this bug that doesn't error

Every guide that covers this error frames it as a parser complaint you fix and move past. That's true on PostgreSQL, SQL Server, and DuckDB (which is what runs the examples on this page). They all enforce the rule strictly. It is not true everywhere.

MySQL can run the invalid query and answer wrong

Older MySQL, and any MySQL or MariaDB instance with ONLY_FULL_GROUP_BY turned off, will execute a query like the broken one at the top of this page without complaint. It doesn't average the ungrouped column or reject it. It picks one row's value per group, arbitrarily, and that choice can change between runs. ONLY_FULL_GROUP_BY has been the default since MySQL 5.7.5, but a lot of production databases still run with it disabled, often because someone hit this exact error years ago and turned the check off instead of fixing the query.

That's the trap: disabling the check doesn't fix anything, it just converts a loud compile-time error into a silent data-correctness bug. The query still does not know which first name belongs in a five-person department; it just stops telling you that and picks one anyway. If a report's numbers are technically valid but the names next to them look randomly assigned, this is usually why. The fix is the same fix as above: aggregate the column or add it to GROUP BY, and ONLY_FULL_GROUP_BY should stay on, not off.

Where to practice this

GROUP BY and its error modes come up constantly once joins and aggregates mix. /learn/group-by covers the clause from first principles if any part of this felt shaky, and the practice problems have several grouping exercises where you can trigger this error yourself and fix it under different constraints before it shows up in a real query.

Cite this resource

SQLSimplified. "Why SQL Says a Column Must Appear in the GROUP BY Clause". Available at: https://sqlsimplified.online/blog/column-must-appear-in-group-by-clause