mahir_data
Chapters0 / 26 completed

Chapter 21 · advanced · 8 min

Query execution order

You've now used SELECT, FROM, WHERE, GROUP BY, HAVING, and ORDER BY, but never asked why some combinations work and others don't. Why can't WHERE filter on an aggregate? Why can ORDER BY use a SELECT alias but WHERE can't? The answer is that SQL executes clauses in a different order than you write them.

We'll use a simple orders table to see the run order in action.

The dataset

An `orders` table: one row per order, with an amount and a region.

Written order vs. run order

You type a query as SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT. SQL actually runs it in this order instead:

StepClauseJob
1FROM / JOINgather the rows
2WHEREfilter individual rows
3GROUP BYform groups
4HAVINGfilter groups
5SELECTcompute output columns, including aggregates and aliases
6DISTINCTde-duplicate
7ORDER BYsort
8LIMITtrim

SELECT is written first but runs almost last. Everything below follows from that one fact.

Why WHERE can't use an aggregate

WHERE runs before GROUP BY; at that point, no aggregate has been computed yet, so writing WHERE SUM(amount) > 150 fails with an error like "aggregate functions are not allowed in WHERE". HAVING runs after grouping, once SUM(amount) actually exists per group, which is exactly why aggregates belong in HAVING, not WHERE:

regiontotal
South245.00
North500.00

The correct, HAVING-based version

Editable, try changing it

Why ORDER BY can use a SELECT alias, but WHERE can't

SELECT computes its aliases before ORDER BY runs, so ORDER BY doubled works, referencing an alias defined in SELECT:

customerdoubled
Farid600.00
Chong400.00
Aisyah240.00
Bala160.00
Devi90.00

But WHERE runs before SELECT even exists yet, so WHERE doubled > 100 would fail: as far as WHERE is concerned, doubled hasn't been computed.

Alias used safely in ORDER BY

Editable, try changing it

Where window functions fit in

Window functions run as part of SELECT, after WHERE/GROUP BY/HAVING, but before ORDER BY/LIMIT. That means you can't filter on a window function's result directly in WHERE (it doesn't exist yet at that stage). The fix is to compute it in a CTE, then filter the outer query, where the window function's result is just an ordinary column:

customeramount
Farid300.00
Chong200.00

Filtering on a window function via a CTE

Editable, try changing it

If you've used Excel or Google Sheets

Spreadsheets don't really have this problem, because a formula in one cell can freely reference another cell's already-computed value regardless of "order"; there's no equivalent gotcha to teach. That's actually a good way to explain why this trips people up coming from Excel: spreadsheets are computed cell-by-cell with automatic dependency resolution, while SQL executes clause-by-clause in a fixed pipeline. Once you know the pipeline, the "why won't this work" questions stop feeling random.

Ready to practice? Grab's above-average drivers question below leans on this exact ordering; it needs a subquery precisely because WHERE can't see an aggregate directly.

Keep this order in your head: FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY, LIMIT, and most of SQL's "why won't this work" moments stop being mysterious. It's also exactly why CTEs and subqueries exist: whenever you need something SELECT computed to be filterable, you wrap it and filter one level up.

Sign in to track your progress.

Now practise it

Questions in the bank that drill this chapter's concept: