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)。

相关推荐
知行合一。。。2 小时前
RAG--03--Milvus基本用法
数据库·oracle·milvus
TDengine (老段)2 小时前
TDengine 线程模型 — 网络、调度、执行
大数据·数据库·物联网·制造·时序数据库·tdengine·涛思数据
上学的小垃圾4 小时前
01-数据库系统概述
数据库
二进制漫游记4 小时前
FastAPI项目集成 Qdrant向量数据库+阿里云Embedding完整实战(工具封装+业务调用)
数据库·python·阿里云·embedding
2501_928996224 小时前
GPT-4o换DeepSeek迁移成本多少?中科热备解析API聚合平台技术账本
前端·数据库·人工智能
泡干脆面就番茄6 小时前
MySQL_子查询_分页查询与联合查询详解
数据库·mysql
devpotato7 小时前
缓存与数据库更新顺序不一致问题
java·数据库·redis
天若有情6737 小时前
Node+MySQL小型全栈笔记项目实战课程分享
数据库·笔记·mysql
wxwx_bscxy3228 小时前
基于springboot宠物领养系统的设计与实现
数据库·spring boot·后端·spring·宠物
九月要晚安8 小时前
达梦DW与AFC对比
数据库