SQL的执行顺序和书写顺序的介绍

SQL 的执行顺序和书写顺序不一样,这是很多人写 SQL 容易困惑的根源。


一、先给结论:书写顺序 vs 执行顺序

书写顺序(你写的样子)

sql 复制代码
SELECT ...      -- 5. 最后选列
FROM ...        -- 1. 先找表
WHERE ...       -- 2. 行级过滤
GROUP BY ...    -- 3. 分组
HAVING ...      -- 4. 组级过滤
ORDER BY ...    -- 6. 排序
LIMIT ...       -- 7. 截断

实际执行顺序(数据库内部)

复制代码
① FROM / JOIN      → 确定数据源,做表连接
② WHERE            → 连接后的结果集,逐行过滤
③ GROUP BY         → 对过滤后的行分组
④ 聚合函数计算      → SUM / COUNT / AVG 等(隐含在 GROUP BY 之后)
⑤ HAVING           → 对分组结果过滤
⑥ SELECT           → 选择输出列(此时才能用聚合结果)
⑦ DISTINCT         → 去重(SELECT 之后)
⑧ ORDER BY         → 对最终结果排序
⑨ LIMIT / OFFSET   → 取前 N 行

二、逐步拆解(配合例子)

示例数据

sql 复制代码
-- orders 表
order_id | user_id | amount | status
---------|---------|--------|--------
1        | 101     | 200    | paid
2        | 101     | 300    | paid
3        | 102     | 150    | unpaid
4        | 103     | 500    | paid
5        | 103     | 100    | paid

查询示例

sql 复制代码
SELECT
    user_id,
    COUNT(*)       AS order_cnt,
    SUM(amount)   AS total_amount
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING SUM(amount) > 300
ORDER BY total_amount DESC
LIMIT 2;

逐步执行过程

① FROM orders

→ 拿到整个 orders 表(5 行)

② WHERE status = 'paid'

→ 过滤掉 status ≠ 'paid' 的行

复制代码
order_id | user_id | amount | status
---------|---------|--------|--------
1        | 101     | 200    | paid
2        | 101     | 300    | paid
4        | 103     | 500    | paid
5        | 103     | 100    | paid

③ GROUP BY user_id

→ 按 user_id 分组

复制代码
组1: user_id=101 → [行1, 行2]
组2: user_id=103 → [行4, 行5]

④ 聚合计算(隐含)

→ 对每个组算 COUNT / SUM

复制代码
user_id=101 → COUNT=2,  SUM=500
user_id=103 → COUNT=2,  SUM=600

⑤ HAVING SUM(amount) > 300

→ 过滤掉聚合结果不满足条件的组

复制代码
user_id=101 → SUM=500 ✅
user_id=103 → SUM=600 ✅

⑥ SELECT

→ 选出 user_id, order_cnt, total_amount 三列输出

⑦ ORDER BY total_amount DESC

→ 按 total_amount 降序排

复制代码
user_id=103 → 600
user_id=101 → 500

⑧ LIMIT 2

→ 取前 2 行(刚好全取)


三、为什么这个顺序很重要?

1. 为什么 WHERE 里不能用聚合函数?

sql 复制代码
-- ❌ 错误
SELECT user_id, SUM(amount)
FROM orders
WHERE SUM(amount) > 300   -- WHERE 执行时还没分组,没有聚合结果
GROUP BY user_id;
sql 复制代码
-- ✅ 正确
SELECT user_id, SUM(amount)
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 300;  -- HAVING 在 GROUP BY 之后,能用聚合结果

2. 为什么 SELECT 里起的别名,WHERE 用不了但 ORDER BY 能用?

sql 复制代码
-- ❌ 错误
SELECT amount * 1.1 AS discounted
FROM orders
WHERE discounted > 200;    -- WHERE 在 SELECT 之前执行,discounted 还不存在
sql 复制代码
-- ✅ 正确
SELECT amount * 1.1 AS discounted
FROM orders
WHERE amount * 1.1 > 200; -- 用原始表达式
sql 复制代码
-- ✅ 正确
SELECT amount * 1.1 AS discounted
FROM orders
ORDER BY discounted;       -- ORDER BY 在 SELECT 之后,可以用别名

3. WHERE vs HAVING 的本质区别

WHERE HAVING
过滤对象 行(分组前) 组(分组后)
能否用聚合函数 ❌ 不能 ✅ 能
性能 ✅ 先过滤,减少分组数据量 分组算完再过滤,开销更大

最佳实践:能写在 WHERE 的条件尽量写 WHERE,减少参与 GROUP BY 的数据量。


四、带 JOIN 的完整执行顺序

sql 复制代码
SELECT u.name, COUNT(o.order_id) AS cnt
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.age > 18
GROUP BY u.user_id, u.name
HAVING COUNT(o.order_id) >= 3
ORDER BY cnt DESC;

执行顺序:

复制代码
① FROM users u LEFT JOIN orders o ON ...  → 先连接,生成笛卡尔积后按条件过滤
② WHERE u.age > 18                        → 连接后的行过滤
③ GROUP BY u.user_id, u.name              → 分组
④ HAVING COUNT(o.order_id) >= 3           → 组过滤
⑤ SELECT                                  → 选列
⑥ ORDER BY                                → 排序

五、记忆口诀

"飞(FROM)屋(WHERE)记(JOIN)得(GROUP BY)好(HAVING),选(SELECT)出(ORDER BY)来(LIMIT)"

或者更直白的理解方式:数据库先把数据找齐(FROM+JOIN),再一行行砍(WHERE),砍完再捆成组(GROUP BY),组里再挑(HAVING),最后选列排序截断(SELECT+ORDER+LIMIT)。

相关推荐
爱喝水的鱼丶1 小时前
SAP-ABAP:S/4 HANA ABAP 新特性入门:与 ECC ABAP 的核心差异、架构优势与学习路径规划
运维·数据库·性能优化·sap·abap·hana·开发交流
余槐i7 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
数据库小学妹8 小时前
PostgreSQL表膨胀怎么办?autovacuum清不动的根因与排查命令
数据库·postgresql·表膨胀·mvcc·数据库运维
mister_guo8 小时前
MySQL-1267错误:排序规则冲突的原因与解决方案
数据库·mysql
骇客野人8 小时前
在麒麟操作系统上 分别安装 jdk1.8, 达梦数据库,redis ,memcache 的操作实施方案和步骤
数据库·redis·memcached
美狐美颜SDK开放平台10 小时前
直播APP如何实现美白滤镜功能?视频美颜SDK技术开发方案详解
前端·数据库·人工智能·音视频·美颜sdk·直播美颜sdk·视频美颜sdk
花间相见11 小时前
【计算基础|MySQL01】【上手】安装连接、数据类型与建库建表
数据库·后端
花间相见11 小时前
【计算基础|MySQL02】【上手】SQL 增删改查
数据库·后端
ITxiaobing202312 小时前
IP 库与 AppsFlyer 国家/地区数据不一致,如何系统地对账?
数据库·网络协议·tcp/ip
这个DBA有点耶12 小时前
从时间到语义:时序数据库与向量数据库的核心机制、选型与多模融合趋势
数据库·mysql·架构