SQL 执行顺序
面试官问:"SQL 的执行顺序是什么?GROUP BY 和 WHERE 谁先执行?"
小陈说:"WHERE 先执行。"
面试官追问:"HAVING 和 WHERE 有什么区别?"
小陈说:"...HAVING 是分组之后用的?"
面试官继续追问:"那 ORDER BY 和 LIMIT 呢?"
小陈:"...ORDER BY 先,然后 LIMIT?"
这道题,考的是候选人对 SQL 执行流程的完整理解。虽然我们写 SQL 是按特定顺序写的,但 MySQL 的执行顺序却完全不同。能说清楚完整执行顺序的候选人,说明他对 SQL 的理解已经到了较深的层次。
一、SQL 编写顺序 vs 执行顺序 🔴
1.1 编写顺序
SELECT DISTINCT column, aggregate_function(column)
FROM table1
JOIN table2 ON condition
WHERE condition
GROUP BY column
HAVING aggregate_function(column)
ORDER BY column ASC/DESC
LIMIT n;
1.2 MySQL 执行顺序
1.3 执行流程图
FROM table
↓
JOIN table2 ON ...
↓
WHERE col = ...
↓
GROUP BY col
↓
aggregate_function()
↓
HAVING ...
↓
SELECT DISTINCT col1, col2, ...
↓
ORDER BY col
↓
LIMIT n
二、关键子句的执行顺序 🔴
2.1 WHERE vs HAVING
WHERE 先执行,HAVING 后执行。
-- WHERE:过滤 FROM/JOIN 后的行
-- HAVING:过滤 GROUP BY 后的分组
SELECT user_id, COUNT(*) as cnt
FROM orders
WHERE status = 1 -- Step 1: 先过滤 status=1 的行
GROUP BY user_id -- Step 2: 再按 user_id 分组
HAVING COUNT(*) > 10; -- Step 3: 最后过滤 count>10 的分组
性能差异:WHERE 在 GROUP BY 之前执行,可以减少参与分组的行数,性能更好。
2.2 WHERE vs ON
SELECT * FROM orders o
JOIN users u ON o.user_id = u.id AND u.status = 1 -- ON 条件
WHERE o.status = 1; -- WHERE 条件
执行顺序:
- ON 条件:先于 JOIN 执行,只影响 JOIN 结果
- WHERE 条件:JOIN 之后执行,影响最终结果
实际差异:
-- LEFT JOIN + ON 条件过滤
SELECT * FROM orders o
LEFT JOIN users u ON o.user_id = u.id AND u.status = 1;
-- 结果:orders 所有行都保留,users 只保留 status=1 的匹配行
-- LEFT JOIN + WHERE 条件过滤
SELECT * FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.status = 1;
-- 结果:users.status=1 的条件在 JOIN 之后,u.status IS NULL 的行被过滤
-- 效果上等价于 INNER JOIN
2.3 SELECT vs ORDER BY
-- SELECT 中定义的别名,ORDER BY 可以使用
SELECT amount * 0.8 as real_amount
FROM orders
ORDER BY real_amount DESC;
-- 执行顺序:
-- 1. SELECT 计算 amount * 0.8
-- 2. ORDER BY 按 real_amount 排序
2.4 ❌ 错误示范
候选人原话:"WHERE 和 HAVING 是一样的,都用来过滤。"
问题诊断:WHERE 在 GROUP BY 之前执行,HAVING 在 GROUP BY 之后执行。WHERE 可以利用索引,HAVING 必须遍历全部分组。
候选人原话 2:"ORDER BY 在 SELECT 之前执行,所以可以用 SELECT 中的别名。"
问题诊断:说反了。ORDER BY 在 SELECT 之后执行,所以可以用 SELECT 中的别名。这正是 ORDER BY 能用别名的原因。
【面试官心理】
这道题看似简单,但能完整说对 11 个步骤的候选人不多。我通常会问:"WHERE 和 ON 的区别在 LEFT JOIN 中有什么表现?"能答出"ON 过滤不影响 LEFT JOIN 的行数,WHERE 过滤影响"的候选人,说明他真正理解了 JOIN 的执行流程。
三、实战优化 🟡
3.1 尽量用 WHERE 而不是 HAVING
-- ❌ 差:HAVING 过滤大量分组
SELECT user_id, COUNT(*) as cnt
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 10; -- GROUP BY 后再过滤,所有分组都要计算
-- ✅ 好:WHERE 提前过滤
SELECT user_id, COUNT(*) as cnt
FROM orders
WHERE status = 1 -- 先过滤,减少参与分组的行数
GROUP BY user_id
HAVING COUNT(*) > 10; -- 只过滤剩余的分组
3.2 索引与执行顺序
-- 索引 idx(status, user_id)
SELECT user_id, COUNT(*)
FROM orders
WHERE status = 1
GROUP BY user_id;
-- 执行计划:
-- 1. WHERE status = 1 使用索引范围扫描
-- 2. 边扫描边按 user_id 分组
-- 3. 不需要回表,利用索引覆盖完成聚合
3.3 LIMIT 与 ORDER BY
-- LIMIT 在 ORDER BY 之后执行
-- 先排序整个结果集,再取前 10 条
-- ❌ 慢:深度分页 + 排序
SELECT * FROM orders ORDER BY id LIMIT 1000000, 10;
-- 排序 1000010 行,取后 10 行
-- ✅ 快:使用索引避免排序
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 10;
-- 利用主键索引的有序性,不需要额外排序
【面试官心理】
执行顺序是 SQL 优化的基础。能理解执行顺序的候选人,才能写出高效的 SQL。比如他知道为什么 WHERE 放在 JOIN 之前能减少数据量,为什么 ORDER BY 配合索引最有效。