Skip to content
PHP2Shell: The PHP Bug
Akshay Thakare

SQL Logical Query Order: Why WHERE Can't See Your Alias

SQL is written SELECT-first but evaluated FROM-first. Knowing the logical order explains alias errors, HAVING vs WHERE, and where window functions run.

SQLBeginner4 min readUpdated 11 Oct 2026

Written order vs evaluated order

You write a query starting with SELECT, but the database evaluates it in a different logical order:

SQL logical evaluation orderSwipe to explore →

Rendering sql logical evaluation order

FROM and JOIN run first, then WHERE, GROUP BY, HAVING, window functions, SELECT, DISTINCT, ORDER BY and finally LIMIT.

This is the logical order — the optimizer may physically reorder work (for example pushing filters into a scan), but the result must be as if this order were followed.

Consequence 1 — WHERE cannot use a SELECT alias

SQL.sql
-- fails in most engines: net_amount does not exist yet when WHERE runs
SELECT amount - discount AS net_amount
FROM orders
WHERE net_amount > 100;
 
-- repeat the expression, or wrap it
SELECT * FROM (
  SELECT amount - discount AS net_amount FROM orders
) t
WHERE net_amount > 100;

Some warehouses (Snowflake, Databricks, BigQuery in some cases) allow aliases in WHERE as an extension. Don't rely on it in portable SQL.

Consequence 2 — WHERE filters rows, HAVING filters groups

SQL.sql
SELECT customer_id, SUM(amount) AS total
FROM orders
WHERE status = 'PAID'          -- row filter: runs before grouping
GROUP BY customer_id
HAVING SUM(amount) > 1000;     -- group filter: runs after aggregation

Consequence 3 — window functions run after GROUP BY

Window functions are computed after grouping and HAVING, just before SELECT finishes. So you can rank aggregated values in one query:

SQL.sql
SELECT customer_id,
       SUM(amount) AS total,
       RANK() OVER (ORDER BY SUM(amount) DESC) AS spend_rank
FROM orders
GROUP BY customer_id;

But you cannot filter on a window function in WHERE — use a subquery or QUALIFY where supported.

Consequence 4 — ORDER BY can use aliases

ORDER BY runs after SELECT, so aliases are visible there:

SQL.sql
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
ORDER BY total DESC;

Interview answer in one breath

SQL is evaluated FROM, WHERE, GROUP BY, HAVING, window functions, SELECT, DISTINCT, ORDER BY, LIMIT. That is why WHERE cannot see a SELECT alias, why aggregates belong in HAVING, and why you filter window results in an outer query or with QUALIFY.

Finished this note?

Share