在 SQL 中,虽然查询的 书写顺序 是 SELECT → FROM → JOIN → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
,但数据库引擎的 实际执行顺序 完全不同。理解执行顺序对编写高效查询和排查问题至关重要。以下是完整的执行顺序及关键逻辑:
SQL 执行顺序详解
**1. FROM
+ JOIN
- 作用:确定数据来源,加载表并处理表连接(如
INNER JOIN
,LEFT JOIN
)。 - 逻辑:
- 先执行
FROM
子句,确定主表。 - 根据
JOIN
条件连接其他表(生成笛卡尔积后按条件筛选)。
- 先执行
- 示例:
FROM tableA LEFT JOIN tableB ON tableA.id = tableB.a_id
**2. WHERE
- 作用:对
FROM
和JOIN
后的中间结果进行行级过滤。 - 注意:
- 无法使用聚合函数(如
SUM()
),因为此时尚未分组。 - 过滤越早,后续处理的数据量越小,性能越高。
- 无法使用聚合函数(如
- 示例:
WHERE tableA.age > 18
**3. GROUP BY
- 作用:将数据按指定列分组,为聚合函数(如
COUNT()
,SUM()
)做准备。 - 逻辑:
- 若未指定
GROUP BY
,整张表视为一个组。 - 分组后,每组只保留一行结果。
- 若未指定
- 示例:
GROUP BY tableA.department
**4. HAVING
- 作用:对分组后的结果进行过滤(类似
WHERE
,但作用于组)。 - 注意:
- 可使用聚合函数(如
HAVING SUM(sales) > 1000
)。 - 必须在
GROUP BY
之后执行。
- 可使用聚合函数(如
- 示例:
HAVING AVG(salary) > 5000
**5. SELECT
- 作用:选择最终输出的列,并计算表达式或聚合函数。
- 关键点:
- 别名(如
AS total
)在此阶段生成,后续步骤(如ORDER BY
)可使用别名。 SELECT *
在此阶段展开为具体列。
- 别名(如
- 示例:
SELECT department, AVG(salary) AS avg_salary
**6. DISTINCT
- 作用:去重(若指定了
DISTINCT
)。 - 逻辑:
- 在
SELECT
之后执行,对最终结果去重。
- 在
- 示例:
SELECT DISTINCT department
**7. ORDER BY
- 作用:对结果排序。
- 注意:
- 可使用
SELECT
中的别名(如avg_salary
)。 - 排序是性能瓶颈,应尽量减少排序数据量。
- 可使用
- 示例:
ORDER BY avg_salary DESC
**8. LIMIT
/ OFFSET
- 作用:限制返回的行数或分页。
- 逻辑:
- 在最终结果上截取指定行。
- 分页时注意性能问题(如
LIMIT 100000, 10
会遍历前 100010 行)。
- 示例:
LIMIT 10
执行顺序总结
执行顺序:
FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT
示例分析
SELECT
department,
AVG(salary) AS avg_salary -- 5. 计算平均工资并命名
FROM employees -- 1. 从 employees 表加载数据
LEFT JOIN departments ON ... -- 1. 连接 departments 表
WHERE hire_date > '2020-01-01' -- 2. 过滤入职时间
GROUP BY department -- 3. 按部门分组
HAVING AVG(salary) > 5000 -- 4. 过滤平均工资低的组
ORDER BY avg_salary DESC -- 7. 按平均工资排序
LIMIT 10; -- 8. 返回前 10 条
常见误区与优化建议
-
WHERE
vsHAVING
:WHERE
在分组前过滤行,HAVING
在分组后过滤组。- 尽量用
WHERE
提前减少数据量,避免HAVING
处理大量分组。
-
别名使用:
GROUP BY
和HAVING
不能使用SELECT
中的别名(因为执行顺序在前)。ORDER BY
可以使用别名。
-
性能优化:
- 在
WHERE
和JOIN
条件中尽量使用索引。 - 避免在
SELECT
或WHERE
中写复杂表达式(可提前计算)。
- 在
执行顺序对结果的影响
- 错误示例:
SELECT department, AVG(salary) AS avg_salary FROM employees WHERE avg_salary > 5000 -- 错误!WHERE 不能使用 SELECT 的别名 GROUP BY department;
- 正确写法应使用
HAVING AVG(salary) > 5000
。
- 正确写法应使用