SQL 的执行顺序和书写顺序不一样,这是很多人写 SQL 容易困惑的根源。
一、先给结论:书写顺序 vs 执行顺序
书写顺序(你写的样子)
sql
SELECT ... -- 5. 最后选列
FROM ... -- 1. 先找表
WHERE ... -- 2. 行级过滤
GROUP BY ... -- 3. 分组
HAVING ... -- 4. 组级过滤
ORDER BY ... -- 6. 排序
LIMIT ... -- 7. 截断
实际执行顺序(数据库内部)
① FROM / JOIN → 确定数据源,做表连接
② WHERE → 连接后的结果集,逐行过滤
③ GROUP BY → 对过滤后的行分组
④ 聚合函数计算 → SUM / COUNT / AVG 等(隐含在 GROUP BY 之后)
⑤ HAVING → 对分组结果过滤
⑥ SELECT → 选择输出列(此时才能用聚合结果)
⑦ DISTINCT → 去重(SELECT 之后)
⑧ ORDER BY → 对最终结果排序
⑨ LIMIT / OFFSET → 取前 N 行
二、逐步拆解(配合例子)
示例数据
sql
-- orders 表
order_id | user_id | amount | status
---------|---------|--------|--------
1 | 101 | 200 | paid
2 | 101 | 300 | paid
3 | 102 | 150 | unpaid
4 | 103 | 500 | paid
5 | 103 | 100 | paid
查询示例
sql
SELECT
user_id,
COUNT(*) AS order_cnt,
SUM(amount) AS total_amount
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING SUM(amount) > 300
ORDER BY total_amount DESC
LIMIT 2;
逐步执行过程
① FROM orders
→ 拿到整个 orders 表(5 行)
② WHERE status = 'paid'
→ 过滤掉 status ≠ 'paid' 的行
order_id | user_id | amount | status
---------|---------|--------|--------
1 | 101 | 200 | paid
2 | 101 | 300 | paid
4 | 103 | 500 | paid
5 | 103 | 100 | paid
③ GROUP BY user_id
→ 按 user_id 分组
组1: user_id=101 → [行1, 行2]
组2: user_id=103 → [行4, 行5]
④ 聚合计算(隐含)
→ 对每个组算 COUNT / SUM
user_id=101 → COUNT=2, SUM=500
user_id=103 → COUNT=2, SUM=600
⑤ HAVING SUM(amount) > 300
→ 过滤掉聚合结果不满足条件的组
user_id=101 → SUM=500 ✅
user_id=103 → SUM=600 ✅
⑥ SELECT
→ 选出 user_id, order_cnt, total_amount 三列输出
⑦ ORDER BY total_amount DESC
→ 按 total_amount 降序排
user_id=103 → 600
user_id=101 → 500
⑧ LIMIT 2
→ 取前 2 行(刚好全取)
三、为什么这个顺序很重要?
1. 为什么 WHERE 里不能用聚合函数?
sql
-- ❌ 错误
SELECT user_id, SUM(amount)
FROM orders
WHERE SUM(amount) > 300 -- WHERE 执行时还没分组,没有聚合结果
GROUP BY user_id;
sql
-- ✅ 正确
SELECT user_id, SUM(amount)
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 300; -- HAVING 在 GROUP BY 之后,能用聚合结果
2. 为什么 SELECT 里起的别名,WHERE 用不了但 ORDER BY 能用?
sql
-- ❌ 错误
SELECT amount * 1.1 AS discounted
FROM orders
WHERE discounted > 200; -- WHERE 在 SELECT 之前执行,discounted 还不存在
sql
-- ✅ 正确
SELECT amount * 1.1 AS discounted
FROM orders
WHERE amount * 1.1 > 200; -- 用原始表达式
sql
-- ✅ 正确
SELECT amount * 1.1 AS discounted
FROM orders
ORDER BY discounted; -- ORDER BY 在 SELECT 之后,可以用别名
3. WHERE vs HAVING 的本质区别
| WHERE | HAVING | |
|---|---|---|
| 过滤对象 | 行(分组前) | 组(分组后) |
| 能否用聚合函数 | ❌ 不能 | ✅ 能 |
| 性能 | ✅ 先过滤,减少分组数据量 | 分组算完再过滤,开销更大 |
最佳实践:能写在 WHERE 的条件尽量写 WHERE,减少参与 GROUP BY 的数据量。
四、带 JOIN 的完整执行顺序
sql
SELECT u.name, COUNT(o.order_id) AS cnt
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.age > 18
GROUP BY u.user_id, u.name
HAVING COUNT(o.order_id) >= 3
ORDER BY cnt DESC;
执行顺序:
① FROM users u LEFT JOIN orders o ON ... → 先连接,生成笛卡尔积后按条件过滤
② WHERE u.age > 18 → 连接后的行过滤
③ GROUP BY u.user_id, u.name → 分组
④ HAVING COUNT(o.order_id) >= 3 → 组过滤
⑤ SELECT → 选列
⑥ ORDER BY → 排序
五、记忆口诀
"飞(FROM)屋(WHERE)记(JOIN)得(GROUP BY)好(HAVING),选(SELECT)出(ORDER BY)来(LIMIT)"
或者更直白的理解方式:数据库先把数据找齐(FROM+JOIN),再一行行砍(WHERE),砍完再捆成组(GROUP BY),组里再挑(HAVING),最后选列排序截断(SELECT+ORDER+LIMIT)。