SQL Query Execution Order
You write SELECT first. PostgreSQL runs it fifth. Here is the actual logical order and why it matters.
Written Order vs. Execution Order
SQL reads top to bottom like English, but that is not the order it logically runs. PostgreSQL processes clauses in a fixed sequence regardless of the order in which they appear on the page.
| id | name | channel | spend | start_date | end_date | status | target_segment |
|---|---|---|---|---|---|---|---|
| 1 | Spring Launch | google_ads | 55000.00 | 2024-01-15 | 2024-03-31 | active | smb |
| 2 | Retention Webinar | 45000.00 | 2024-02-10 | 2024-04-15 | active | enterprise | |
| 3 | Finance Retargeting | 50000.00 | 2024-03-12 | 2024-05-31 | active | enterprise | |
| 4 | Enterprise Search | google_ads | 60000.00 | 2024-04-01 | 2024-06-30 | active | enterprise |
SELECT
channel,
AVG(spend) AS avg_spend
FROM marketing.campaigns
WHERE status = 'active'
GROUP BY channel
HAVING AVG(spend) > 48000
ORDER BY avg_spend DESC
LIMIT 2;Watch the query run against Queryflo campaign data. The numbered step shows the clause being evaluated, not the line currently being read.
| name | channel | spend | status |
|---|---|---|---|
| Spring Launch | google_ads | 55,000.00 | active |
| Retention Webinar | 45,000.00 | active | |
| Finance Retargeting | 50,000.00 | active | |
| Enterprise Search | google_ads | 60,000.00 | active |
FROM loads all four rows from marketing.campaigns before any condition is checked.
Why This Matters
Two consequences follow directly from the logical order:
- You cannot reference a
SELECTalias insideWHERE, because WHERE runs before SELECT creates that alias. - You can reference the alias inside
ORDER BY, because SELECT has already run by then.
Practice this concept
Marketing wants the two active campaign channels whose average spend is greater than 48,000, ordered from highest to lowest average spend.
marketingPrefix tables with marketing.table_name.
channelavg_spendmarketing.campaigns| Column | Type |
|---|---|
| id | integer |
| name | text |
| channel | text |
| spend | numeric |
| start_date | date |
| end_date | date |
| status | text |
| target_segment | text |
| legacy_id | text |
Sign up free to try it on a real business scenario