The short answer

GROUP BY comes before ORDER BY — no exceptions. This is true two ways at once: it's the required syntax order, and it's the logical execution order. Group the rows, then sort the groups.

correct-order.sql
-- ✅ Group first, then sort the grouped result
SELECT department, COUNT(*) AS headcount
FROM employees
GROUP BY department
ORDER BY headcount DESC;
syntax-error.sql
-- ❌ Syntax error: ORDER BY cannot come before GROUP BY
SELECT department, COUNT(*)
FROM employees
ORDER BY department
GROUP BY department;

Written order vs execution order

SQL requires clauses in a fixed sequence, and evaluates them in a different (but consistent) sequence. In both, GROUP BY precedes ORDER BY:

Required written orderLogical execution order
SELECTFROM / JOIN
FROMWHERE
WHEREGROUP BY
GROUP BYHAVING
HAVINGSELECT
ORDER BYORDER BY
LIMITLIMIT

Since ORDER BY runs after SELECT, it can "see" everything the query has already produced — grouped columns, aggregates, and aliases. For the full pipeline, see SQL execution order explained.

What you CAN sort by after GROUP BY

In a grouped query, ORDER BY may reference:

  • Any column in the GROUP BY list (e.g. ORDER BY department).
  • Any aggregate (e.g. ORDER BY COUNT(*) DESC, ORDER BY SUM(amount)).
  • Any alias defined in SELECT (e.g. ORDER BY headcount).
  • An ordinal position (e.g. ORDER BY 2) — though naming the column or alias is clearer.

Sorting by an aggregate (top-N groups)

Sorting by an aggregate is how you build "top N" reports. Combine it with LIMIT:

top-n.sql
-- Five departments with the most employees
SELECT department, COUNT(*) AS headcount
FROM employees
GROUP BY department
ORDER BY COUNT(*) DESC
LIMIT 5;

Note that LIMIT runs after ORDER BY, so it correctly takes the top 5 of the sorted result — not five random groups.

Sorting by a SELECT alias

Because ORDER BY is evaluated after SELECT, aliases you created there are available — the opposite of WHERE, which runs before SELECT and can't see them.

alias.sql
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
ORDER BY avg_salary DESC;   -- alias works in ORDER BY

What you can't ORDER BY

The grouping rule applies to ORDER BY too: a column that isn't in GROUP BY and isn't aggregated has no single value per group, so you can't sort by it.

cant.sql
-- ❌ salary isn't grouped or aggregated
SELECT department, COUNT(*)
FROM employees
GROUP BY department
ORDER BY salary;

-- ✅ Sort by an aggregate of salary instead
SELECT department, COUNT(*)
FROM employees
GROUP BY department
ORDER BY AVG(salary) DESC;

GROUP BY doesn't sort

A frequent assumption is that GROUP BY returns groups in sorted order "for free." It doesn't — grouping and sorting are separate operations, and the order you happen to get can change with indexes or engine versions. If you need a specific order, always add ORDER BY. (More on this in GROUP BY mistakes that break SQL queries.)

Full clause order reference

ClausePurposeMust be written…
WHEREFilter rowsbefore GROUP BY
GROUP BYForm groupsafter WHERE
HAVINGFilter groupsafter GROUP BY
ORDER BYSort the resultafter HAVING
LIMIT / OFFSETTrim the resultlast

Key takeaways

  • ORDER BY always comes after GROUP BY — in syntax and in execution.
  • Writing ORDER BY before GROUP BY is a syntax error.
  • You can sort by grouped columns, aggregates, and SELECT aliases.
  • You can't sort by a column that's neither grouped nor aggregated.
  • GROUP BY never sorts — add ORDER BY to guarantee order; LIMIT runs last.