SQL Order of Execution

When you first start writing SQL, it feels pretty straightforward. You write a SELECT, point it at a table with FROM, maybe filter it with WHERE, and you’re off.

But once you start adding aggregations, grouping, and filtering on those aggregations, things can get confusing quickly. One of the most common issues people run into is not understanding the order in which SQL actually processes a query.

Even though we write SQL in a certain order, the database doesn’t execute it in that same sequence. Getting your head around this is key to avoiding errors and writing more efficient queries.


The Order We Write SQL

When writing a query, we always follow this structure:

  • SELECT
  • FROM
  • WHERE
  • GROUP BY
  • HAVING
  • ORDER BY

This is the standard format you’ll see everywhere, and it’s how SQL is designed to be written.


The Order SQL Actually Runs

Here’s where things get interesting. The database does not execute your query in the same order you write it. Instead, it processes it like this:

  1. FROM
  2. WHERE
  3. GROUP BY
  4. HAVING
  5. SELECT
  6. ORDER BY

This difference is exactly why certain things “don’t work” when you first try them.


Why This Is Important

1. You Can’t Use Aggregates in WHERE

A very common mistake is trying something like this:

SELECT customer_id, SUM(sales)
FROM orders
WHERE SUM(sales) > 1000
GROUP BY customer_id

This won’t work. WHERE runs before GROUP BY, and therefore before the SUM() is calculated. Instead, you need to use HAVING, which runs after aggregation:

SELECT customer_id, SUM(sales)
FROM orders
GROUP BY customer_id
HAVING SUM(sales) > 1000


2. WHERE Filters Rows, HAVING Filters Groups

Because of the execution order:

  • WHERE filters individual rows before grouping
  • HAVING filters aggregated results after grouping

Think of it like this:

  • WHERE decides what data goes into the calculation
  • HAVING decides which results come out of the calculation

3. Aliases Don’t Work Everywhere

Another confusing one is column aliases. You might write:

SELECT SUM(sales) AS total_sales
FROM orders
WHERE total_sales > 1000

This fails because SELECT runs after WHERE, so total_sales doesn’t exist yet. But this works:

SELECT SUM(sales) AS total_sales
FROM orders
HAVING SUM(sales) > 1000


A Simple Way to Remember It

If you’re ever stuck, just remember:

  • SQL reads like:
    SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY
  • But SQL runs like:
    FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY

That mental model will save you a lot of debugging time.


Final Thoughts

Understanding the order of execution in SQL is one of those foundational concepts that unlocks everything else. Once it clicks, a lot of the “weird” errors you run into suddenly make sense. It also helps you write cleaner, more intentional queries, especially when working with aggregations and building more complex logic. Learning this, combined with the basic SQL commands I covered in my last blog, is key to learning SQL.

Author:
Martin Regan
Powered by The Information Lab
1st Floor, 25 Watling Street, London, EC4M 9BR
Subscribe
to our Newsletter
Get the lastest news about The Data School and application tips
Subscribe now
© 2026 The Information Lab