Understanding SQL Execution Order
You write SQL in one order, but the database engine processes it in a completely different sequence! Understanding this underlying execution logic—especially the difference between WHERE and HAVING—is the foundational key to mastering data filtering and avoiding common errors.
Written Order (Syntax)
This is how we type the query. It is designed to read like a natural English sentence, making it easy for humans to formulate.
Execution Order (Logic)
This is how the database engine actually processes the data step-by-step to optimize performance and apply logic correctly.
Interactive Filter Funnel
Click through the steps below to see exactly how data flows and transforms through each clause of the query.
FROM Employees
WHERE Salary > 3500
GROUP BY Dept
HAVING COUNT(*) > 1;
Current Data State
Key Differences: WHERE vs HAVING
WHERE
- Filters individual rows before any grouping occurs.
- Cannot use aggregate functions (e.g.,
SUM(),COUNT(),AVG()). - Example:
WHERE Salary > 3500(Checks each person's salary).
HAVING
- Filters groups after
GROUP BYis applied. - Primarily used with aggregate functions to evaluate group totals/averages.
- Example:
HAVING COUNT(*) > 1(Checks the size of the group).
Programming Integration
See how to execute this exact SQL query within application code using SQLite.
import sqlite3
# 1. Connect to the SQLite database
conn = sqlite3.connect('company.db')
cursor = conn.cursor()
# 2. Define the SQL query (The database engine handles the execution order automatically)
query = """
SELECT Dept, AVG(Salary)
FROM Employees
WHERE Salary > 3500
GROUP BY Dept
HAVING COUNT(*) > 1;
"""
# 3. Execute the query
cursor.execute(query)
# 4. Fetch and display the results
results = cursor.fetchall()
for row in results:
print(f"Department: {row[0]}, Average Salary: {row[1]}")
# 5. Close the connection
conn.close()
#include <iostream>
#include <sqlite3.h>
// Callback function to process each row of the result set
int callback(void* NotUsed, int argc, char** argv, char** azColName) {
std::cout << "Department: " << argv[0]
<< ", Average Salary: " << argv[1] << std::endl;
return 0;
}
int main() {
sqlite3* db;
// 1. Open database connection
sqlite3_open("company.db", &db);
// 2. Define the SQL query
const char* query = R"(
SELECT Dept, AVG(Salary)
FROM Employees
WHERE Salary > 3500
GROUP BY Dept
HAVING COUNT(*) > 1;
)";
char* errMsg = 0;
// 3. Execute query and pass the callback function to handle results
sqlite3_exec(db, query, callback, 0, &errMsg);
// 4. Clean up and close connection
sqlite3_close(db);
return 0;
}
Knowledge Check
1. Which clause is executed FIRST by the database engine?
2. I want to filter out departments that have an average salary below 4000. Which clause should I use?