news 2026/4/16 18:14:26

【SQL】SQL 语句的解析顺序:理解查询执行的逻辑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【SQL】SQL 语句的解析顺序:理解查询执行的逻辑

文章目录

    • 一、SQL 解析顺序:为什么不是按书写顺序执行
    • 二、FROM 子句:确定数据源和表连接
    • 三、WHERE 子句:过滤行数据
    • 四、GROUP BY 子句:数据分组
    • 五、HAVING 子句:过滤分组
    • 六、SELECT 子句:处理表达式和去重
    • 七、ORDER BY 子句:最终排序
    • 八、完整示例:理解整个执行流程
      • 执行步骤详解
      • 关键理解点

SQL 语句的执行顺序与书写顺序不同,理解解析顺序有助于编写高效的查询、排查问题,以及理解为什么某些写法会报错或性能差。SQL 解析按照 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 的顺序进行,每个步骤都会生成一个虚拟表。

核心要点

  1. FROM 子句最先执行,包括表的连接和 ON 过滤
  2. WHERE 在 GROUP BY 之前执行,用于过滤行
  3. HAVING 在 GROUP BY 之后执行,用于过滤分组
  4. SELECT 在大部分子句之后执行,处理表达式和去重
  5. ORDER BY 最后执行,对最终结果排序

一、SQL 解析顺序:为什么不是按书写顺序执行

SQL 语句按照以下顺序解析和执行:

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY

为什么是这个顺序

  • FROM 最先:需要先确定数据源,才能进行后续操作
  • **WHERE :先过滤行,再分组,效率更高
  • HAVING:只能对分组后的结果进行过滤
  • SELECT 靠后:大部分操作完成后,再处理表达式和去重
  • ORDER BY 最后:对最终结果进行排序

![[Pasted image 20231227170316.png]]


二、FROM 子句:确定数据源和表连接

FROM 子句最先执行,确定查询的数据源,包括表的连接操作。

FROM 的执行步骤

1. 确定数据源

  • FROM 后面的表标识了查询的数据源
  • 生成虚拟表 VT1

2. 表的连接(如果有多表)

(1-J1)笛卡尔积

  • 计算两个相关联表的笛卡尔积(CROSS JOIN)
  • 生成虚拟表 VT1-J1
  • 如果没有 JOIN 条件,会产生所有可能的组合

(1-J2)ON 过滤

  • 基于虚拟表 VT1-J1 进行过滤
  • 过滤出所有满足 ON 谓词条件的行
  • 生成虚拟表 VT1-J2
  • 注意:ON 条件只影响连接,不影响最终结果的行数(外连接除外)

(1-J3)添加外部行

  • 如果使用了外连接(LEFT/RIGHT/FULL JOIN)
  • 保留表中不符合 ON 条件的行也会被加入
  • 作为外部行,生成虚拟表 VT1-J3
-- 假设有两个表:users (id, name) 和 orders (id, user_id, amount)SELECTu.name,o.amountFROMusers uLEFTJOINorders oONu.id=o.user_idWHEREo.amount>100;

执行过程:FROM users 生成 VT1(users 表的所有行),LEFT JOIN orders 计算笛卡尔积生成 VT1-J1,ON u.id = o.user_id 过滤连接条件生成 VT1-J2,添加外部行保留 users 中没有匹配的行生成 VT1-J3,WHERE o.amount > 100 过滤最终结果。


三、WHERE 子句:过滤行数据

WHERE 子句在 FROM 之后执行,对虚拟表 VT1 进行过滤,满足条件的行被插入到 VT2。

WHERE 的作用

  • 过滤行:基于行的条件进行过滤
  • 在分组之前:先过滤行,再分组,减少需要处理的数据量
  • 不能使用聚合函数:因为此时还没有分组
SELECTdepartment,AVG(salary)FROMemployeesWHEREsalary>5000-- WHERE 先执行,过滤掉 salary <= 5000 的行GROUPBYdepartment;

执行过程:FROM employees 生成 VT1(所有员工),WHERE salary > 5000 过滤生成 VT2(只包含 salary > 5000 的员工),GROUP BY department 对 VT2 分组生成 VT3。

为什么 WHERE 在 GROUP BY 之前:先过滤行,减少数据量;如果先分组再过滤,需要处理更多数据,效率低。


四、GROUP BY 子句:数据分组

GROUP BY 子句对 WHERE 过滤后的表进行分组,生成分组后的虚拟表 VT3。

GROUP BY 的作用

  • 分组:按照指定的列对数据进行分组
  • 生成分组:每个不同的组合生成一个组
  • 为聚合函数准备:分组后可以使用聚合函数(COUNT、SUM、AVG 等)
SELECTdepartment,COUNT(*)asemp_count,AVG(salary)asavg_salaryFROMemployeesWHEREsalary>5000GROUPBYdepartment;

执行过程:FROM employees 生成 VT1,WHERE salary > 5000 生成 VT2,GROUP BY department 生成 VT3(按部门分组,每个部门一个组,每个组包含该部门的所有员工)。


五、HAVING 子句:过滤分组

HAVING 子句在 GROUP BY 之后执行,对分组后的结果进行过滤,满足条件的分组被加入到 VT4。

HAVING 与 WHERE 的区别

特性WHEREHAVING
执行时机GROUP BY 之前GROUP BY 之后
作用对象分组
可以使用聚合函数
效率更高(先过滤行)较低(后过滤分组)
SELECTdepartment,AVG(salary)asavg_salaryFROMemployeesWHEREsalary>5000-- 先过滤行GROUPBYdepartmentHAVINGAVG(salary)>8000;-- 再过滤分组(平均工资 > 8000 的部门)

执行过程:FROM → WHERE → GROUP BY 生成 VT3(分组后的结果),HAVING AVG(salary) > 8000 生成 VT4(只保留平均工资 > 8000 的部门)。

为什么需要 HAVING:WHERE 不能使用聚合函数,需要在分组后过滤分组结果。


六、SELECT 子句:处理表达式和去重

SELECT 子句在大部分操作之后执行,处理表达式、去重和限制结果数量。

SELECT 的执行步骤

(5-1)计算表达式

  • 计算 SELECT 子句中的表达式
  • 包括列名、函数、计算等
  • 生成虚拟表 VT5-1

(5-2)DISTINCT

  • 寻找 VT5-1 中的重复行
  • 删除重复行
  • 生成虚拟表 VT5-2

(5-3)TOP/LIMIT

  • 从结果中筛选出符合条件的行数
  • 生成虚拟表 VT5-3
SELECTDISTINCTdepartment,COUNT(*)asemp_countFROMemployeesWHEREsalary>5000GROUPBYdepartmentHAVINGCOUNT(*)>10;

执行过程:FROM → WHERE → GROUP BY → HAVING 生成 VT4,SELECT 计算表达式生成 VT5-1(计算 COUNT(*)),DISTINCT 去重生成 VT5-2(如果有重复),TOP/LIMIT 限制数量生成 VT5-3。

为什么 SELECT 靠后执行:需要先完成过滤、分组等操作,再处理表达式和去重,这就是为什么 WHERE 中不能使用 SELECT 中的别名。


七、ORDER BY 子句:最终排序

ORDER BY 子句最后执行,对最终结果进行排序,生成最终结果表 VC6。

ORDER BY 的特点

  • 最后执行:在所有其他操作完成后执行
  • 可以使用别名:可以使用 SELECT 中定义的别名
  • 影响性能:排序操作通常比较耗时
  • 可选:不是必需的子句
SELECTdepartment,AVG(salary)asavg_salaryFROMemployeesWHEREsalary>5000GROUPBYdepartmentHAVINGAVG(salary)>8000ORDERBYavg_salaryDESC;-- 可以使用 SELECT 中的别名

执行过程:FROM → WHERE → GROUP BY → HAVING → SELECT 生成 VT5-3,ORDER BY avg_salary DESC 生成 VC6(最终结果,按平均工资降序排列)。

为什么 ORDER BY 可以使用别名:因为 ORDER BY 在 SELECT 之后执行,SELECT 中定义的别名已经可用。


八、完整示例:理解整个执行流程

SELECTdepartment,COUNT(*)asemp_count,AVG(salary)asavg_salaryFROMemployeesWHEREsalary>5000GROUPBYdepartmentHAVINGCOUNT(*)>10ORDERBYavg_salaryDESCLIMIT5;

执行步骤详解

FROM:从 employees 表读取所有数据,生成虚拟表 VT1。

WHERE:过滤 salary > 5000 的行,生成虚拟表 VT2(只包含工资 > 5000 的员工)。

GROUP BY:按 department 分组,生成虚拟表 VT3(每个部门一个组)。

HAVING:过滤 COUNT(*) > 10 的分组,生成虚拟表 VT4(只保留员工数 > 10 的部门)。

SELECT:计算表达式 COUNT(*) 和 AVG(salary),生成 VT5-1;如果有 DISTINCT 则去重生成 VT5-2;LIMIT 5 限制数量生成 VT5-3。

ORDER BY:按 avg_salary DESC 排序,生成最终结果表 VC6。

关键理解点

  1. WHERE 中不能使用别名:因为 WHERE 在 SELECT 之前执行
  2. HAVING 可以使用聚合函数:因为 HAVING 在 GROUP BY 之后执行
  3. ORDER BY 可以使用别名:因为 ORDER BY 在 SELECT 之后执行
  4. 先过滤再分组:WHERE 在 GROUP BY 之前,效率更高

参考资源

  • SQL 解析顺序详细示例
  • 代数与物理优化基础
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 15:00:46

道岔故障智能诊断与预警系统研究

目录 第一章 绪论 1.1 研究背景与意义 1.2 国内外研究现状 1.2.1 国外研究现状 1.2.2 国内研究现状 1.3 主要研究内容与技术路线 第二章 道岔故障机理分析 2.1 道岔结构与工作原理 2.2 典型故障模式分析 2.2.1 机械故障 2.2.2 电气故障 2.2.3 环境因素故障 2.3 故障…

作者头像 李华
网站建设 2026/4/16 11:00:09

短视频直播平台搭建必备:直播美颜SDK与动态贴纸开发/接入详解

在短视频和直播行业的快速发展中&#xff0c;如何让平台更具吸引力、提升用户互动体验成为了开发者和运营者必须关注的核心问题。尤其是对直播平台和短视频平台来说&#xff0c;直播美颜SDK与动态贴纸SDK的引入&#xff0c;已经成为提升用户粘性、增强平台活跃度的重要手段。本…

作者头像 李华
网站建设 2026/4/16 10:44:00

Vue+Laravel全栈开发实战指南

环境配置确保已安装Node.js&#xff08;用于Vue开发&#xff09;和Composer&#xff08;用于Laravel依赖管理&#xff09;。Laravel项目可通过以下命令创建&#xff1a;composer create-project laravel/laravel project-name前端项目初始化在Laravel项目根目录下初始化Vue2项目…

作者头像 李华
网站建设 2026/4/16 4:08:39

MySQL中如何进行SQL调优?

SQL调优的核心思路是减少磁盘I/O和避免无效计算。 主要就是先通过MySQL的慢查询日志定位慢SQL&#xff0c;再利用EXPLAIN分析执行计划&#xff0c;最后再进行针对性优化。 优化的手段主要有这几大类&#xff0c;分别是索引层面的优化&#xff0c;SQL写法层面的优化以及架构层…

作者头像 李华
网站建设 2026/4/6 0:20:00

C语言输入与输出(I/O)全面解析

C语言输入与输出(I/O)全面解析 引言 C语言作为一种历史悠久、功能强大的编程语言,其输入与输出(I/O)功能是编程中不可或缺的部分。本文将全面解析C语言的输入与输出,包括标准输入输出函数、文件操作、格式化输出等,帮助读者深入理解C语言I/O机制。 标准输入输出函数 …

作者头像 李华
网站建设 2026/4/16 12:02:01

SQL NOT NULL约束详解

SQL NOT NULL约束详解 概述 在SQL数据库中,NOT NULL约束是一种常用的数据完整性约束,用于确保数据库表中的字段不会存储任何NULL值。本文将详细介绍NOT NULL约束的概念、用法以及它在数据库设计中的重要性。 什么是NOT NULL约束 NOT NULL约束是一种完整性约束,用于指定一…

作者头像 李华