了解 SQL 的执行顺序非常有价值,它可以让我们写出语法正确的 SQL,帮助我们简化编写新查询的过程。
本文将在 MySQL 的基础上,介绍查询语句的执行顺序。下面是我们经常看到的查询语句的语法结构:
SELECT <select_list>FROM <left_table><join_type> JOIN <right_table>ON <join_condition>WHERE <where_condition>GROUP BY <group_by_list>HAVING <having_condition>ORDER BY <order_by_list>LIMIT <limit_number>
它的执行顺序:
FROM
/ JOIN
和所有 ON
条件WHERE
GROUP BY
HAVING
SELECT
ORDER BY
LIMIT
以上是 SQL 标准定义的执行顺序。实际上,如果是简单的单表查询,即查询语句里面只包含了一张表,它将严格按照定义的执行顺序执行查询。对于多表查询,数据库有的时候并没有按此顺序运行查询,因为它们实现了一系列优化使查询运行更快。这些优化可能会改变实际的执行顺序,但它们最终必须返回与以默认的执行顺序运行查询的结果相同。
按照执行顺序的规则,排在后面的子句产生的结果不能被前面的子句引用。比如:
WHERE
子句里面使用 SELECT
子句设置的别名,因为 WHERE
子句先于 SELECT
子句执行;# 执行失败,提示“Unknown column 'ename_job' in 'where clause'”SELECT CONCAT(ename, '|', job) AS ename_job FROM emp WHERE ename_job = 'KING|PRESIDENT'
2. 不能在 WHERE
子句里面过滤 GROUP BY
子句的聚合结果,因为 WHERE
子句先于 GROUP BY
子句执行;
# 执行不通过,提示“Invalid use of group function”SELECT deptno, COUNT(*) AS cnt FROM emp WHERE COUNT(*) > 0GROUP BY deptno
3. 可以在 ORDER BY
子句中引用 GROUP BY
子句聚合的结果,因为 ORDER BY
子句的执行顺序在 GROUP BY
子句之后。
# 执行成功SELECT deptno, COUNT(*) FROM emp GROUP BY deptno ORDER BY COUNT(*)
在 MySQL 里面,我们会看到一些“反常”的操作。
比如下面这条 SQL ,看起来像是 SELECT
子句的别名被 GROUP BY
子句引用。
SELECT CONCAT(job, '|', deptno) AS job_dept, COUNT(*) FROM emp GROUP BY job_dept
那是不是说这条 SQL 破坏了前面定义的执行顺序呢?实际上并没有,MYSQL 会对这条 SQL 做重写,像这样:
SELECT CONCAT(job, '|', deptno), COUNT(*) FROM emp GROUP BY CONCAT(job, '|', deptno)
再有,比如下面这条多表关联查询的语句。
SELECT e.*, d.dname FROM emp e LEFT JOIN dept d ON d.deptno = e.deptno WHERE e.job = 'PRESIDENT'
假如 emp 表和 dept 都是很大的表,emp 通过条件 job = 'PRESIDENT'
过滤后会得到很小的结果集。如果按照标准的执行顺序先执行两个大表的 LEFT JOIN
再执行 WHERE
过滤,那整个 JOIN 操作将会占用很大的内存。MySQL 可能会对 emp 表先执行WHERE
子句的过滤操作,过滤后的结果集再和 dept 表关联。