sql语句执行流程
SQL语句的完整执行顺序详解
SQL语句的逻辑书写顺序与实际执行顺序是不同的。理解这个执行顺序对于编写高效查询和优化性能至关重要。
一、SQL语句的书写顺序(我们写的顺序)
SELECT column1, column2, aggregate(column3)
FROM table1JOIN table2 ON table1.id = table2.id
WHERE condition1
GROUP BY column1, column2
HAVING aggregate(column3) > value
ORDER BY column1 ASC, column2 DESC
LIMIT offset, count;
二、SQL语句的实际执行顺序
- FROM 和 JOIN(确定数据来源)
- WHERE(行级过滤)
- GROUP BY(分组)
- HAVING(组级过滤)
- SELECT(选择列)
- DISTINCT(去重)
- ORDER BY(排序)
- LIMIT/OFFSET(分页)
三、详细执行步骤解释(以具体例子说明)
示例查询:
SELECT d.department_name,COUNT(e.employee_id) AS emp_count,AVG(e.salary) AS avg_salary
FROM departments dLEFT JOIN employees e ON d.department_id = e.department_id
WHERE e.hire_date > '2020-01-01'
GROUP BY d.department_name
HAVING COUNT(e.employee_id) > 5
ORDER BY avg_salary DESC
LIMIT 10;
执行步骤分解:
-
FROM & JOIN 阶段(最先执行)
FROM departments d LEFT JOIN employees e ON d.department_id = e.department_id
- 先加载
departments
表所有数据 - 然后执行LEFT JOIN,关联
employees
表 - 此时生成一个虚拟的中间结果集(包含两表所有列)
- 先加载
-
WHERE 阶段(行级过滤)
WHERE e.hire_date > '2020-01-01'
- 对上一步的中间结果集进行过滤
- 只保留hire_date大于2020-01-01的记录
- 注意:WHERE在GROUP BY之前执行,所以不能使用聚合函数
-
GROUP BY 阶段(分组)
GROUP BY d.department_name
- 将过滤后的数据按部门名称分组
- 此时每个分组包含多条员工记录
-
HAVING 阶段(组级过滤)
HAVING COUNT(e.employee_id) > 5
- 对分组后的结果进行过滤
- 只保留员工数大于5的部门
- 注意:HAVING可以使用聚合函数,WHERE不能
-
SELECT 阶段(选择列)
SELECT d.department_name,COUNT(e.employee_id) AS emp_count,AVG(e.salary) AS avg_salary
- 现在才确定最终返回哪些列
- 计算每个部门的员工数和平均薪资
- 为结果列指定别名(emp_count, avg_salary)
-
DISTINCT 阶段(去重)
- 如果查询包含DISTINCT,在此阶段去重
- 本例中没有DISTINCT
-
ORDER BY 阶段(排序)
ORDER BY avg_salary DESC
- 使用SELECT阶段创建的别名排序
- 按平均薪资降序排列
- 注意:可以使用列别名,因为ORDER BY在SELECT之后执行
-
LIMIT 阶段(分页)
LIMIT 10
- 只返回前10条记录
- 最后执行的操作
四、关键注意事项
-
执行顺序解释为什么WHERE不能使用SELECT别名:
-- 错误示例 SELECT column1 AS c1 FROM table WHERE c1 > 10; -- 正确写法 SELECT column1 AS c1 FROM table WHERE column1 > 10;
- WHERE执行时SELECT还没执行,所以不知道别名
-
HAVING与WHERE的区别:
-- WHERE过滤行(GROUP BY之前) -- HAVING过滤组(GROUP BY之后) SELECT dept, AVG(salary) FROM employees WHERE hire_date > '2020-01-01' -- 先过滤员工 GROUP BY dept HAVING AVG(salary) > 5000; -- 再过滤部门
-
ORDER BY可以使用SELECT别名:
SELECT column1 + column2 AS total FROM table ORDER BY total; -- 合法
五、复杂查询的执行顺序示例
SELECT t1.name,COUNT(t2.order_id) AS order_count
FROM customers t1JOIN orders t2 ON t1.customer_id = t2.customer_id
WHERE t2.order_date BETWEEN '2023-01-01' AND '2023-12-31'AND t1.country = 'USA'
GROUP BY t1.name
HAVING COUNT(t2.order_id) >= 3
ORDER BY order_count DESC
LIMIT 5;
执行流程:
- 从
customers
表获取所有客户数据 - 关联
orders
表,通过customer_id匹配 - 过滤出2023年的订单且客户国家为USA的记录
- 按客户姓名分组
- 筛选出订单数≥3的客户组
- 计算每个客户的订单数
- 按订单数降序排序
- 只返回前5条记录
六:哪些操作会产生中间表
SELECT d.name, COUNT(e.id)
FROM departments d
JOIN employees e ON d.id = e.dept_id -- [中间表1] 两表连接结果
WHERE e.salary > 5000 -- [中间表2] 过滤后结果
GROUP BY d.name -- [中间表3] 分组后结果
HAVING COUNT(e.id) > 10 -- [中间表4] 组过滤后结果
ORDER BY COUNT(e.id) DESC -- [中间表5] 排序后结果
LIMIT 5; -- 最终结果
-
LIMIT/OFFSET:
- 只是从已有结果集中截取部分行
- 不改变数据内容,不产生新中间表
-
SELECT列选择:
- 只是从中间结果集中投影部分列
- 不改变行数据,只是减少列数