Why your SQL alias errors: the execution order
You write queries top to bottom. The engine runs them in a different order. That one fact explains the alias error, WHERE vs HAVING, and more.
Get the free PDF
One page, print-ready, free to share. No signup needed.
You wrote the query top to bottom. The engine runs it in a completely different order. That single fact explains the most common beginner error in SQL, the WHERE vs HAVING confusion, and why ORDER BY is special.
The error everyone hits
SELECT price * quantity AS revenue
FROM sales
WHERE revenue > 1000;
-- ERROR: column 'revenue' does not exist
The alias exists in your head, not yet in the engine. WHERE runs before SELECT, so at filter time revenue has not been created.
Written order vs run order
| You write | Engine runs |
|---|---|
| 1. SELECT | 1. FROM + JOINs |
| 2. FROM | 2. WHERE |
| 3. WHERE | 3. GROUP BY |
| 4. GROUP BY | 4. HAVING |
| 5. HAVING | 5. SELECT (aliases born) |
| 6. ORDER BY | 6. ORDER BY (aliases visible) |
ORDER BY is the only clause that can use your aliases: it is the only one that runs after SELECT.
The two fixes
-- 1. repeat the expression
SELECT price * quantity AS revenue
FROM sales
WHERE price * quantity > 1000;
-- 2. or name it in a CTE first
WITH t AS (
SELECT *, price * quantity AS revenue
FROM sales
)
SELECT * FROM t WHERE revenue > 1000;
Some engines (BigQuery, DuckDB) also offer QUALIFY and lateral column aliases, but the CTE works everywhere.
The trap: WHERE vs HAVING
WHERE filters rows before grouping. HAVING filters groups after.
-- wrong: aggregate in WHERE
SELECT city, SUM(amount)
FROM sales
WHERE SUM(amount) > 100 -- ERROR
GROUP BY city;
-- right: aggregates live in HAVING
SELECT city, SUM(amount)
FROM sales
GROUP BY city
HAVING SUM(amount) > 100;
Same root cause: WHERE runs before GROUP BY, so no aggregate exists yet when it fires. Once the execution order clicks, half of SQL's error messages start making sense.
Frequently asked questions
- Why can't I use a column alias in WHERE?
- Because WHERE runs before SELECT. At the moment the engine filters rows, the alias has not been created yet. Repeat the expression in WHERE, or define it in a CTE and filter the CTE.
- What is the SQL order of execution?
- FROM and JOINs first, then WHERE, then GROUP BY, then HAVING, then SELECT (where aliases are born), then ORDER BY, then LIMIT. ORDER BY is the only major clause that can see your SELECT aliases.
- What is the difference between WHERE and HAVING?
- WHERE filters individual rows before grouping, so it cannot contain aggregates. HAVING filters whole groups after GROUP BY, so SUM, COUNT and friends belong there.
- Why does my alias work in ORDER BY but not in GROUP BY?
- ORDER BY runs after SELECT, so aliases exist by then. GROUP BY runs before SELECT, so standard SQL says no aliases there. Some engines (MySQL, Postgres, BigQuery) allow it as an extension, which is why the same query works on one database and fails on another.
Get the free PDF
One page, print-ready, free to share. No signup needed.
