SQL Group By with an Order By
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
GROUP BY and ORDER BY solve different problems, but they are commonly used in the same report query. GROUP BY decides how rows are collapsed into summary groups, while ORDER BY decides how those summary rows are presented at the end. Once that separation is clear, most confusing SQL errors around aggregation become much easier to diagnose.
Understand the Query Stages First
A grouped query is easier to reason about if you think in stages instead of reading it top to bottom. In broad terms, the database engine first gathers rows, then filters them, then forms groups, then calculates aggregates, and only after that sorts the final result set.
A typical pattern looks like this:
The important consequence is that ORDER BY sees the grouped result, not the original raw rows. That is why sorting by revenue is valid here even though revenue does not exist in the base table. It is an alias produced by aggregation.
Grouping Controls Which Columns Are Legal
The most common GROUP BY mistake is selecting a column that is neither grouped nor aggregated. Once rows are collapsed into one row per group, there is no single value available for any ungrouped column unless you tell the database how to summarize it.
Invalid example:
If one customer has ten orders, which created_at value should the engine return. SQL has no deterministic answer unless you specify one.
Valid alternatives are:
Here the grouped row has well-defined values, so ordering by one of those aggregates is safe.
Order By Aggregates and Aliases
Once grouping is complete, you can order by grouped columns, aggregate expressions, or their aliases depending on the SQL dialect.
You can also write ORDER BY SUM(total_amount) DESC, but the alias is usually easier to read and maintain. In production queries it is also worth adding a tie-breaker so output order remains deterministic when two groups have the same revenue.
That small secondary sort saves a lot of confusion in dashboards, exports, and tests.
Use HAVING for Group Filters
Another frequent source of confusion is deciding whether a condition belongs in WHERE or HAVING. WHERE filters individual rows before grouping. HAVING filters whole groups after the aggregates have been calculated.
This query first removes non-winning rows, then groups the remaining rows by salesperson, then discards groups with fewer than five wins, and finally orders the remaining summary rows.
A simple rule is:
- use
WHEREwhen the condition can be evaluated per row - use
HAVINGwhen the condition depends on an aggregate
Do Not Abuse GROUP BY for Top Row Per Group
People often try to use GROUP BY when the real problem is “give me the highest-value row for each customer.” That is not a normal aggregation problem, because you need one full row, not a summary of many rows. A window function is usually the correct tool.
This solves a very common reporting request that cannot be expressed cleanly with a plain grouped select.
Performance Still Matters
Grouped queries often end with a sort, and sorts can become expensive on large tables. The main performance levers are not mysterious:
- reduce the input set early with
WHERE - index columns used for filtering and joining
- keep the grouped key set as small as the business question allows
- avoid selecting unnecessary derived values
If the same report runs constantly on very large data, a pre-aggregated summary table may be a better design than asking the database to group raw events every time.
Common Pitfalls
- Selecting columns that are neither part of
GROUP BYnor wrapped in an aggregate. - Assuming
ORDER BYsorts raw rows before grouping happens. - Using
HAVINGfor filters that should have been applied earlier inWHERE. - Forgetting tie-breaker columns, which produces unstable output order when aggregate values match.
- Trying to fetch a representative detail row with
GROUP BYwhen a window function is the correct approach.
Summary
- '
GROUP BYdefines the summary grain of the result set.' - '
ORDER BYsorts the grouped output after aggregates are computed.' - Every selected column in a grouped query must be grouped or aggregated.
- '
HAVINGfilters groups, whileWHEREfilters source rows.' - For “top row per group” problems, use window functions instead of forcing
GROUP BYto do the wrong job.
Related reading
- SQL How to perform string does not equal
- SQL Identity autonumber is Incremented Even with a Transaction Rollback
- SQL injection that gets around mysql_real_escape_string
- SQL JOIN what is the difference between WHERE clause and ON clause?
- SQL JPA - Multiple columns as primary key
- SQL keys, MUL vs PRI vs UNI
- SQL multiple column ordering
- sql multithreading application select and delete from a table

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.