当前位置: 首页 > news >正文

MySQL中SELECT查询的执行顺序

MySQL中SELECT查询的执行顺序

在日常的数据库开发中,我们经常会写各种复杂的SELECT查询语句。然而,很多开发者对于MySQL实际执行这些查询的顺序并不完全了解。理解查询的执行顺序不仅有助于编写更高效的SQL语句,还能帮助我们更好地优化查询性能和避免常见的错误。

一、SELECT语句的书写顺序 vs 执行顺序

首先,让我们明确一个重要概念:SQL语句的书写顺序和实际执行顺序是不同的

书写顺序

SELECT DISTINCT column_list
FROM table_list
JOIN table ON join_condition
WHERE where_condition
GROUP BY column_list
HAVING having_condition
ORDER BY column_list
LIMIT count OFFSET offset

实际执行顺序

FROM -> JOIN -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT

二、详解各步骤的执行顺序

让我们通过一个具体的例子来深入理解每个步骤:

SELECT DISTINCT d.dept_name,COUNT(e.emp_id) as emp_count,AVG(e.salary) as avg_salary
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
WHERE e.hire_date >= '2020-01-01'
GROUP BY d.dept_name
HAVING COUNT(e.emp_id) > 5
ORDER BY avg_salary DESC
LIMIT 10;

1. FROM 子句

执行顺序:第一步

MySQL首先确定数据来源,从指定的表中读取所有数据。

FROM employees e

此时会创建一个虚拟表VT1,包含employees表的所有行。

2. JOIN 子句

执行顺序:第二步

如果有JOIN操作,MySQL会根据连接条件合并表。

INNER JOIN departments d ON e.dept_id = d.dept_id
  • 生成笛卡尔积
  • 应用ON条件进行过滤
  • 根据JOIN类型(INNER/LEFT/RIGHT)决定保留哪些行
  • 生成虚拟表VT2

3. WHERE 子句

执行顺序:第三步

对JOIN后的结果集进行条件过滤。

WHERE e.hire_date >= '2020-01-01'

注意:WHERE子句不能使用聚合函数,因为此时还没有进行分组。生成虚拟表VT3。

4. GROUP BY 子句

执行顺序:第四步

按指定列对数据进行分组。

GROUP BY d.dept_name
  • 将VT3中的数据按dept_name分组
  • 每个分组变成结果集中的一行
  • 生成虚拟表VT4

5. HAVING 子句

执行顺序:第五步

对分组后的数据进行过滤。

HAVING COUNT(e.emp_id) > 5
  • HAVING可以使用聚合函数
  • 只保留员工数大于5的部门
  • 生成虚拟表VT5

6. SELECT 子句

执行顺序:第六步

选择要返回的列,执行表达式计算。

SELECT d.dept_name,COUNT(e.emp_id) as emp_count,AVG(e.salary) as avg_salary
  • 计算聚合函数
  • 执行表达式
  • 应用列别名
  • 生成虚拟表VT6

7. DISTINCT 子句

执行顺序:第七步

去除重复的行。

SELECT DISTINCT ...

生成虚拟表VT7。

8. ORDER BY 子句

执行顺序:第八步

对结果集进行排序。

ORDER BY avg_salary DESC
  • 可以使用SELECT中定义的别名
  • 可以使用未在SELECT中出现的列(如果该列在分组中)
  • 生成虚拟表VT8

9. LIMIT 子句

执行顺序:第九步

限制返回的行数。

LIMIT 10

最终返回前10条记录。

三、理解执行顺序的重要性

1. 别名的使用限制

由于执行顺序的原因,列别名在不同位置的可用性不同:

-- 错误示例:WHERE中不能使用SELECT定义的别名
SELECT salary * 12 as annual_salary
FROM employees
WHERE annual_salary > 50000;  -- 错误!-- 正确示例:
SELECT salary * 12 as annual_salary
FROM employees
WHERE salary * 12 > 50000;-- 或者使用子查询
SELECT * FROM (SELECT salary * 12 as annual_salaryFROM employees
) t
WHERE annual_salary > 50000;

2. WHERE vs HAVING

理解执行顺序可以帮助我们正确使用WHERE和HAVING:

-- WHERE:过滤行(分组前)
-- HAVING:过滤组(分组后)-- 错误:WHERE中使用聚合函数
SELECT dept_id, AVG(salary)
FROM employees
WHERE AVG(salary) > 5000  -- 错误!
GROUP BY dept_id;-- 正确:HAVING中使用聚合函数
SELECT dept_id, AVG(salary)
FROM employees
GROUP BY dept_id
HAVING AVG(salary) > 5000;-- 性能优化:尽可能使用WHERE
SELECT dept_id, AVG(salary)
FROM employees
WHERE salary > 3000  -- 先过滤,减少分组的数据量
GROUP BY dept_id
HAVING AVG(salary) > 5000;

3. JOIN的优化

理解JOIN在WHERE之前执行,可以帮助我们优化查询:

-- 低效:先JOIN所有数据,再WHERE过滤
SELECT *
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date >= '2024-01-01';-- 高效:在JOIN条件中尽早过滤
SELECT *
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id AND o.order_date >= '2024-01-01';

四、特殊情况和注意事项

1. 子查询的执行顺序

子查询的执行时机取决于其类型:

-- 非相关子查询:先执行子查询
SELECT * 
FROM employees 
WHERE dept_id IN (SELECT dept_id FROM departments WHERE location = 'Beijing'
);-- 相关子查询:对外查询的每一行执行一次
SELECT e1.* 
FROM employees e1
WHERE salary > (SELECT AVG(salary) FROM employees e2 WHERE e2.dept_id = e1.dept_id
);

2. UNION的执行顺序

(SELECT name FROM employees WHERE dept_id = 1)
UNION
(SELECT name FROM employees WHERE dept_id = 2)
ORDER BY name;
  • 先执行各个SELECT
  • 然后合并结果(去重)
  • 最后应用ORDER BY

3. 窗口函数的执行顺序

窗口函数在SELECT阶段执行,但在DISTINCT之前:

SELECT DISTINCTdept_id,salary,ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) as rn
FROM employees
WHERE hire_date >= '2020-01-01';

执行顺序:FROM -> WHERE -> SELECT(包括窗口函数) -> DISTINCT

五、性能优化建议

基于执行顺序的理解,我们可以得出以下优化建议:

1. 尽早过滤数据

-- 在WHERE中过滤,而不是HAVING
-- 在JOIN条件中过滤,而不是WHERE

2. 合理使用索引

-- 为WHERE、JOIN、ORDER BY涉及的列创建索引
CREATE INDEX idx_hire_date ON employees(hire_date);
CREATE INDEX idx_dept_id ON employees(dept_id);

3. 避免在WHERE中使用函数

-- 不好
WHERE YEAR(hire_date) = 2024-- 好
WHERE hire_date >= '2024-01-01' AND hire_date < '2025-01-01'

4. 使用EXPLAIN分析执行计划

EXPLAIN SELECT ... ;

六、常见误区总结

  1. 误区:认为SQL按书写顺序执行 事实:执行顺序是固定的,与书写顺序不同
  2. 误区:WHERE和HAVING可以互换使用 事实:WHERE过滤行,HAVING过滤组,执行时机不同
  3. 误区:SELECT中定义的别名可以在WHERE中使用 事实:WHERE在SELECT之前执行,无法使用别名
  4. 误区:ORDER BY总是最后执行 事实:如果有LIMIT,ORDER BY在LIMIT之前执行

结语

深入理解MySQL SELECT查询的执行顺序是编写高效SQL的基础。通过掌握这些知识,我们可以:

  • 避免常见的SQL错误
  • 编写更高效的查询
  • 更好地进行性能优化
  • 理解查询结果的生成过程

记住核心执行顺序:FROM -> JOIN -> WHERE -> GROUP BY -> HAVING -> SELECT -> DISTINCT -> ORDER BY -> LIMIT

http://www.xdnf.cn/news/768673.html

相关文章:

  • 【 HarmonyOS 5 入门系列 】鸿蒙HarmonyOS示例项目讲解
  • SCAU8643--简单选择排序
  • 2506js,活扩控件
  • 基于ubuntu和树莓派环境对游戏进行移植
  • cJSON简单使用
  • uniapp路由跳转toolbar页面
  • 为何ERP系统更倾向使用业务编码作为主键?兼顾可读性与系统集成的设计思考
  • 【Zephyr 系列 3】多线程与调度机制:让你的 MCU 同时干多件事
  • oracle sql 语句 优化方法
  • 数据库系统概论(十三)详细讲解SQL中数据更新(插入,修改与更新)
  • RocketMQ 消息发送核心源码解析:DefaultMQProducerImpl.send () 方法深度剖析
  • 前端开发知识体系全景指南
  • 小目标检测:YOLOV7改进之双坐标注意力(DCA)
  • Python Day41
  • 神经网络与深度学习(第一章)
  • 链式前向星图解
  • 排序算法C语言实现
  • Linux配置DockerHub镜像源配置
  • Qt实现的水波进度条和温度进度条
  • 神经网络中的梯度消失与梯度爆炸
  • cnn训练并用grad-cam可视化
  • 基于遥感图像深度学习的海洋测深
  • 2024年数维杯国际大学生数学建模挑战赛C题时间信号脉冲定时噪声抑制与大气时延抑制模型解题全过程论文及程序
  • 题目 3230: 蓝桥杯2024年第十五届省赛真题-星际旅行
  • [蓝桥杯]约瑟夫环
  • web架构2------(nginx多站点配置,include配置文件,日志,basic认证,ssl认证)
  • 2025年5月24日系统架构设计师考试题目回顾
  • 【RAG 应用的可视化框架】
  • 【C++】类的构造函数
  • 【iOS(swift)笔记-13】App版本不升级时本地数据库sqlite更新逻辑一