🔄 SQL Execution Order

Understanding how SQL processes your queries step by step

The Logical Order of SQL Execution

SQL doesn't execute in the order you write it! Here's the actual sequence:

1
FROM
SQL starts by identifying which table(s) to retrieve data from. This is where the database locates the source data.
2
WHERE
Next, SQL filters the rows based on your conditions. Only rows that meet the WHERE criteria move forward.
3
SELECT
Now SQL determines which columns to include in the result set. This is where column aliases are defined.
4
ORDER BY
Finally, SQL sorts the result set. This is always the last step, so you can use column aliases from SELECT here.
📋 Table Data
🔍 Filter Rows
📊 Pick Columns
🔢 Sort Results

💡 Step-by-Step Example

Let's see how SQL processes this query:

SELECT name, salary * 12 AS annual_salary FROM employees WHERE department = 'Sales' ORDER BY annual_salary DESC;
Step 1: FROM employees

SQL starts with the entire employees table:

id name department salary
1 Alice Sales 5000
2 Bob Engineering 6000
3 Charlie Sales 5500
4 Diana Marketing 4800
5 Eve Sales 4500
Step 2: WHERE department = 'Sales'

SQL filters to keep only Sales department rows:

id name department salary
1 Alice Sales 5000
2 Bob Engineering 6000
3 Charlie Sales 5500
4 Diana Marketing 4800
5 Eve Sales 4500

✓ Highlighted rows pass the filter | ✗ Strikethrough rows are excluded

Step 3: SELECT name, salary * 12 AS annual_salary

SQL selects only the requested columns and calculates annual_salary:

name annual_salary
Alice 60000
Charlie 66000
Eve 54000
Step 4: ORDER BY annual_salary DESC

SQL sorts the results in descending order by annual_salary:

name annual_salary
Charlie 66000
Alice 60000
Eve 54000

🎉 Final Result: Sorted from highest to lowest salary!

Try It Yourself in SQL Editor →

📊 GROUP BY & HAVING - Advanced Example

Let's explore how GROUP BY and HAVING fit into the execution order:

SELECT department, COUNT(*) AS emp_count, AVG(salary) AS avg_salary FROM employees WHERE salary > 4000 GROUP BY department HAVING COUNT(*) >= 2 ORDER BY avg_salary DESC;
Step 1: FROM employees

SQL starts with the entire employees table:

id name department salary
1 Alice Sales 5000
2 Bob Engineering 6000
3 Charlie Sales 5500
4 Diana Marketing 3500
5 Eve Sales 4500
6 Frank Engineering 6500
7 Grace Engineering 5800
Step 2: WHERE salary > 4000

SQL filters rows based on individual row conditions (before grouping):

id name department salary
1 Alice Sales 5000
2 Bob Engineering 6000
3 Charlie Sales 5500
4 Diana Marketing 3500
5 Eve Sales 4500
6 Frank Engineering 6500
7 Grace Engineering 5800

✓ Diana is excluded (salary 3500 ≤ 4000)

Step 3: GROUP BY department

SQL groups rows by department. Each group becomes one row in the result:

📦 Sales Group

name salary
Alice5000
Charlie5500
Eve4500

Count: 3 | Avg: 5000

📦 Engineering Group

name salary
Bob6000
Frank6500
Grace5800

Count: 3 | Avg: 6100

💡 Marketing group was excluded earlier by WHERE clause, so it doesn't appear here.

Step 4: HAVING COUNT(*) >= 2

SQL filters the groups based on aggregate conditions:

department emp_count avg_salary
Sales 3 5000
Engineering 3 6100

✓ Both groups have 3 employees (>= 2), so both pass the HAVING filter

Step 5: SELECT department, COUNT(*) AS emp_count, AVG(salary) AS avg_salary

SQL selects the specified columns with aggregations and aliases:

department emp_count avg_salary
Sales 3 5000
Engineering 3 6100
Step 6: ORDER BY avg_salary DESC

SQL sorts the results by average salary in descending order:

department emp_count avg_salary
Engineering 3 6100
Sales 3 5000

🎉 Final Result: Engineering department has the highest average salary!

Practice GROUP BY and HAVING →

🔍 WHERE vs HAVING - Key Differences

WHERE Clause

Timing: Executes before GROUP BY

Purpose: Filters individual rows

Works with: Column values

Cannot use: Aggregate functions

HAVING Clause

Timing: Executes after GROUP BY

Purpose: Filters grouped results

Works with: Aggregate functions

Can use: COUNT, SUM, AVG, etc.

💡 Quick Rule of Thumb:

  • Use WHERE to filter rows before grouping (e.g., "salary > 5000")
  • Use HAVING to filter groups after grouping (e.g., "COUNT(*) > 10")
  • You can use both in the same query!

🔑 Key Insights

📝 Written Order

The order you write SQL clauses: SELECT → FROM → WHERE → ORDER BY

⚙️ Execution Order

The order SQL actually processes: FROM → WHERE → SELECT → ORDER BY

💡 Why It Matters

Understanding execution order helps you debug queries and use aliases correctly.

🎯 Alias Usage

You can use SELECT aliases in ORDER BY because SELECT runs first!

⚠️ Common Mistakes

❌ Wrong: Using alias in WHERE

SELECT salary * 12 AS annual_salary FROM employees WHERE annual_salary > 50000; -- ❌ Error!

This fails because WHERE executes before SELECT, so the alias doesn't exist yet!

✅ Correct: Repeat the calculation

SELECT salary * 12 AS annual_salary FROM employees WHERE salary * 12 > 50000; -- ✅ Works!

In WHERE, you must use the original expression, not the alias.

✅ Correct: Use alias in ORDER BY

SELECT salary * 12 AS annual_salary FROM employees WHERE salary * 12 > 50000 ORDER BY annual_salary DESC; -- ✅ Works!

ORDER BY executes after SELECT, so you can use the alias here!

🚀 Complete Execution Order (All Clauses)

When your query includes GROUP BY, HAVING, and LIMIT, here's the full execution sequence:

1
FROM & JOIN
Identify and join tables
2
WHERE
Filter individual rows
3
GROUP BY
Group rows together
4
HAVING
Filter grouped results
5
SELECT
Choose columns to return
6
ORDER BY
Sort the results
7
LIMIT / OFFSET
Restrict number of rows returned
Practice in SQL Editor →