SQL查询语句的执行顺序

好的,我们来详细讲解一下 SQL 查询语句的执行顺序
很多人会误以为 SQL 的执行顺序就是我们写的顺序(SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY),但实际上,数据库引擎在底层处理查询时,遵循一个完全不同的逻辑顺序。理解这个顺序对于编写高效、正确的 SQL 查询至关重要。

一、核心执行顺序(逻辑查询处理顺序)

这是 SQL 查询在数据库内部被处理的逻辑步骤。每一步都会产生一个虚拟表,作为下一步的输入。
我们以一个完整的查询为例:

SELECT DISTINCT column1, column2
FROM table1
JOIN table2 ON table1.id = table2.id
WHERE condition
GROUP BY column1
HAVING group_condition
ORDER BY column2 DESC
LIMIT n;

它的实际执行顺序如下:

第 1 步:FROM / JOIN
  • 作用:确定需要查询的数据来源。数据库引擎首先会执行 FROM 子句,如果有关联的表(JOIN),它会根据 ON 条件将这些表连接起来,形成一个大的虚拟表(VT1)。
  • 为什么是第一步:因为所有后续的操作(筛选、分组、排序)都必须基于一个完整的数据集。
第 2 步:WHERE
  • 作用:对 FROM 步骤生成的虚拟表(VT1)进行行级过滤。根据 WHERE 子句中的条件,移除不满足条件的行,生成一个新的虚拟表(VT2)。
  • 关键点WHERE 子句在分组和聚合之前执行。因此,它不能使用聚合函数(如 COUNT(), SUM(), AVG())。如果你尝试在 WHERE 中使用聚合函数,数据库会报错。
  • 性能提示:这是进行数据筛选最重要的环节,尽早过滤掉无用数据可以大大减少后续步骤的计算量。
第 3 步:GROUP BY
  • 作用:根据 GROUP BY 子句中指定的列,将 WHERE 步骤过滤后的虚拟表(VT2)中的数据进行分组。具有相同分组列值的行会被合并到一组,生成一个新的虚拟表(VT3)。
  • 结果:此时的虚拟表由多个“组”构成,每一组代表一个唯一的分组键值。
第 4 步:HAVING
  • 作用:对 GROUP BY 步骤生成的分组进行过滤。它类似于 WHERE,但作用于而不是单行。根据 HAVING 子句中的条件,移除不满足条件的组,生成一个新的虚拟表(VT4)。
  • 关键点HAVING分组之后执行,因此它可以使用聚合函数(如 HAVING COUNT(*) > 5)。这是 HAVINGWHERE 最本质的区别。
第 5 步:SELECT
  • 作用:这是最容易被误解的一步。直到这一步,数据库才开始真正“选择”需要返回的列。它会遍历 HAVING 步骤生成的虚拟表(VT4),并只保留 SELECT 子句中明确指定的列(或表达式),生成一个新的虚拟表(VT5)。
  • 关键点
    • 别名生效:在 SELECT 步骤中定义的列别名(如 SELECT salary * 12 AS annual_salary),在这一步之后才生效。这就是为什么在 WHEREGROUP BY 中不能使用 SELECT 中定义的别名,但在 ORDER BY 中却可以的原因。
    • 表达式计算:在 SELECT 中定义的表达式(如数学运算、函数调用)也是在这一步进行计算的。
第 6 步:DISTINCT
  • 作用:对 SELECT 步骤生成的虚拟表(VT5)进行去重。移除所有完全相同的行,生成一个新的虚拟表(VT6)。
  • 性能DISTINCT 操作通常需要排序或哈希,可能会消耗较多资源,应谨慎使用。
第 7 步:ORDER BY
  • 作用:对最终的结果集(DISTINCT 步骤后的虚拟表 VT6)进行排序。根据 ORDER BY 子句中指定的列和排序方式(ASCDESC)对行进行排序。
  • 关键点
    • 最后一步之一ORDER BY 是在几乎所有数据处理完成后才执行的。
    • 可以使用别名:因为 ORDER BYSELECT 之后执行,所以它可以引用 SELECT 中定义的列别名。
第 8 步:LIMIT / OFFSET / TOP
  • 作用:这是整个查询的最后一步。它从排序好的结果集中,限制返回的行数。
    • LIMIT n:返回前 n 行。
    • OFFSET m LIMIT n:跳过前 m 行,返回接下来的 n 行。
    • TOP n:返回前 n 行。
  • 性能提示LIMIT 通常在分页查询中使用。但请注意,如果查询中包含了 ORDER BY,数据库需要先对所有符合条件的数据进行排序,然后再应用 LIMIT,这在数据量很大时可能会很慢。

二、执行顺序与书写顺序的对比

执行顺序子句作用描述书写顺序
1FROM, JOIN确定数据源,连接表2
2WHERE过滤行(在分组前)3
3GROUP BY对行进行分组4
4HAVING过滤组(在分组后)5
5SELECT选择列,计算表达式,定义别名1
6DISTINCT去重1 (在SELECT后)
7ORDER BY对最终结果排序6
8LIMIT限制返回的行数7

三、为什么理解执行顺序很重要?(实例说明)

1. 为什么 WHERE 中不能用聚合函数,而 HAVING 可以?
-- 错误的写法
SELECT department, COUNT(*) as emp_count
FROM employees
WHERE COUNT(*) > 5  -- 报错!因为 WHERE 在 GROUP BY 之前执行,此时 COUNT(*) 还不存在
GROUP BY department;
-- 正确的写法
SELECT department, COUNT(*) as emp_count
FROM employees
GROUP BY department  -- 先分组
HAVING COUNT(*) > 5; -- 再对组进行过滤
2. 为什么 ORDER BY 可以使用 SELECT 中的别名,而 WHERE 不可以?
-- 错误的写法
SELECT first_name, salary * 12 AS annual_salary
FROM employees
WHERE annual_salary > 60000; -- 报错!因为 WHERE 在 SELECT 之前执行,别名 'annual_salary' 还不存在
-- 正确的写法
SELECT first_name, salary * 12 AS annual_salary
FROM employees
WHERE salary * 12 > 60000; -- 在 WHERE 中重复写表达式
-- 或者,利用执行顺序,在 ORDER BY 中使用别名
SELECT first_name, salary * 12 AS annual_salary
FROM employees
WHERE salary > 5000 -- 先用原始列过滤
ORDER BY annual_salary DESC; -- 再用别名排序(因为 ORDER BY 在 SELECT 之后)

总结

记住这个核心顺序:FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT
将 SQL 的执行顺序想象成一条流水线,数据从源头(FROM)开始,经过一道道工序(WHERE, GROUP BY 等)的加工和筛选,最终形成我们想要的产品(结果集)。理解了这个流程,你就能更清晰地思考问题,写出逻辑正确、性能更优的 SQL 查询。


SELECT 中的非聚合列必须出现在 GROUP BY 子句中,否则会报错。

HAVING 子句用于过滤聚合函数的结果(如 SUM、COUNT、AVG 等)。
g.weight < 50 是对原始列的过滤,不属于聚合条件,应该放在 WHERE 子句中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93866.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93866.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Android】OKHttp网络请求原理和弱网优化

【Android】OKHttp网络请求原理和弱网优化 1. OkHttp 网络请求原理 OkHttp 的请求过程可以分为 四个关键阶段&#xff1a; &#xff08;假设你是通过 OkHttpClient.newCall(request).enqueue(callback) 发的请求&#xff09; OkHttpClient│▼ Dispatcher (调度器)│▼ RealC…

概率论基础教程第4章 随机变量(四)

4.7 泊松随机变量 定义 泊松随机变量&#xff1a;如果一个取值于 $ 0, 1, 2, \ldots $ 的随机变量对某一个 $ \lambda > 0 $&#xff0c;其分布列为&#xff1a; p(i)P{Xi}e−λλii!i0,1,2,⋯(7.1) \boxed{p(i) P\{X i\} e^{-\lambda} \frac{\lambda^i}{i!} \qquad i 0…

Unity高级开发:反射原理深入解析与实践指南 C#

Unity高级开发&#xff1a;反射原理深入解析与实践指南 在Unity游戏开发中&#xff0c;反射&#xff08;Reflection&#xff09; 是一项强大的元编程技术&#xff0c;它允许程序在运行时动态地获取类型信息、创建对象和调用方法。根据Unity官方统计&#xff0c;超过78%的商业游…

任务五 推荐页面功能开发

一、推荐页面需求分析 由推荐页面效果图,可以看出,推荐页面主要由顶部轮播图和歌单列表页面组成 二、推荐页面轮播图组件封装 由于轮播图,可能在项目多个地方用到,因此可以将轮播图抽调成一个组件,然后各个页面调用这个组件。 在开发轮播图组件时,需要安装better-scro…

【工具使用-Docker容器】构建自己的镜像和容器

1. 镜像和容器介绍 镜像&#xff08;Image&#xff09;是一个只读的模板&#xff0c;包含了运行某个应用所需的全部内容&#xff0c;比如&#xff1a; 操作系统&#xff08;比如 Ubuntu&#xff09;应用程序代码运行环境&#xff08;如 Python、Java、Node.js 等&#xff09;库…

Apache Shiro550 漏洞(CVE-2016-4437):原理剖析与实战 SOP

在 Web 安全领域&#xff0c;反序列化漏洞一直是威胁等级极高的存在&#xff0c;而 Apache Shiro 框架中的 Shiro550 漏洞&#xff08;CVE-2016-4437&#xff09;&#xff0c;更是因利用门槛低、影响范围广&#xff0c;成为渗透测试中频繁遇到的经典漏洞。本文将从 “原理拆解”…

安卓开发者自学鸿蒙开发3持久化/数据与UI绑定

AppStorage,PersistentStorage与StorageLink AppStorage是应用全局状态管理器,数据存储于内存中,常见的如全局的黑暗模式,StorageLink是用来绑定AppStorage的键到ui上的工具,省去了用户手写代码的无聊过程,PersistentStorage可以绑定AppStorage的键,自动持久化到磁盘,同时支持多…

GitHub宕机生存指南:从应急协作到高可用架构设计

GitHub宕机生存指南&#xff1a;从应急协作到高可用架构设计 摘要&#xff1a; GitHub作为全球开发者的协作中心&#xff0c;其服务稳定性至关重要。然而&#xff0c;任何在线服务都无法保证100%的可用性。本文深入探讨了当GitHub意外宕机时&#xff0c;开发团队应如何应对。我…

机器学习算法篇(十三)------词向量转化的算法思想详解与基于词向量转换的文本数据处理的好评差评分类实战(NPL基础实战)

目录 一、词向量原理介绍 (1). 词向量的核心概念 (2). 传统文本表示的局限性 1. 独热编码&#xff08;One-Hot Encoding&#xff09; 2. 词袋模型&#xff08;Bag of Words&#xff09; 3. TF-IDF (3). 词向量的核心原理 (4). 主流词向量模型 1. Word2Vec&#xff08;20…

JS自定义函数(2)

1. 变量的作用域全局变量定义&#xff1a;在函数外声明的变量作用范围&#xff1a;在整个JS文档中生效生命周期&#xff1a;页面关闭时销毁局部变量定义&#xff1a;在函数内用 var 声明的变量作用范围&#xff1a;只能在函数内部使用生命周期&#xff1a;函数执行完毕时销毁作…

【数据集】Argoverse 数据集:自动驾驶研究的强大基石

Argoverse数据集&#xff1a;自动驾驶研究的强大基石 在自动驾驶技术蓬勃发展的当下&#xff0c;高质量的数据集对于推动相关算法研究和模型训练起着举足轻重的作用。Argoverse 数据集便是其中的佼佼者&#xff0c;它为自动驾驶领域的众多任务提供了丰富且优质的数据资源。 一、…

--- 哈希表和哈希冲突 ---

哈希&#xff08;散列&#xff09;方法是对插入的数据通过哈希函数计算出一个哈希地值&#xff0c;并将这个哈希地址作为储存改数据的地址&#xff0c;这样下次再查找这个数据时&#xff0c;只需要通过哈希函数再获取到该地址然后直接去拿就好这样就做到了不经过任何比较&#…

数学建模-评价类问题-优劣解距离法(TOPSIS)

1-AI带你认识TOPSIS&#x1f4d8; 一、TOPSIS 方法简介1. ​​基本定义&#xff1a;​​​​TOPSIS&#xff08;Technique for Order Preference by Similarity to an Ideal Solution&#xff09;​​&#xff0c;中文通常称为&#xff1a;•​​优劣解距离法​​•​​逼近理想…

Go协程:从汇编视角揭秘实现奥秘

&#x1f680; Go协程&#xff1a;从汇编视角揭秘实现奥秘 #Go语言 #协程原理 #并发编程 #底层实现 引用&#xff1a; 关于 Go 协同程序&#xff08;Coroutines 协程&#xff09;、Go 汇编及一些注意事项。 &#x1f31f; 前言&#xff1a;重新定义并发编程范式 在当今高并发…

MySQL 事务(重点)

MySQL 这个东西注定是可能会被多个用户/客户端来同时访问的&#xff0c;这是肯定的&#xff0c;MySQL 中存放的都是数据&#xff0c;数据可能有一个上层线程在用&#xff0c;也有可能另一个线程也要用...数据是被所有人共享的&#xff0c;所以就注定了 MySQL 这样的服务在一个时…

uniapp:h5链接拉起支付宝支付

场景&#xff1a;APP内点击支付宝支付&#xff0c;后台返回类似链接https://qr.alipay.com/bax***********c3050 通常做法是&#xff0c;使用plus.runtime.openURL(deeplink);先打开浏览器&#xff0c;浏览器会提示打开支付宝&#xff0c;之后是支付流程。现在可以省略跳转h5的…

吴恩达 Machine Learning(Class 3)

Week 11.1 K-means Cluster centroidK-means 是无监督学习中聚类算法的一种&#xff0c;核心在于更新聚类质心&#xff1b;首先将每个点分配给几个聚类质心&#xff0c;取决于那些点离哪个质心更近&#xff1b;然后将几个聚类质心移动到分配给他的所有点的平均值&#xff0c;不…

MyBatis 动态查询语句详解:让 SQL 更灵活可控

MyBatis 动态查询语句详解&#xff1a;让 SQL 更灵活可控 在日常的数据库操作中&#xff0c;我们经常会遇到需要根据不同条件拼接 SQL 语句的场景。比如查询用户时&#xff0c;可能需要根据姓名、年龄、性别等多个条件进行筛选&#xff0c;而这些条件往往是动态变化的 —— 有时…

Java基础语法three

一、一维数组一维数组初始化数据类型[] 数组名new 数据类型[数组长度]//动态初始化数据类型[] 数组名new 数据类型[]{值}//静态初始化数据类型[] 数组名{值}数组长度一旦确定&#xff0c;就不可更改。数组是序排序&#xff1b;数组属于引用数据类型的变量&#xff0c;数组的元素…

【数据结构】排序算法全解析:概念与接口

1.排序的概念及其运用 1.1 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的…