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

相关推荐
Poo_Chai1 小时前
QT emit信号后完整处理流程,包括槽函数响应流程
java·开发语言·数据库
茶本无香2 小时前
Java调用Shell脚本执行SQL数据库操作:从入门到实战
java·sql·shell
这个DBA有点耶2 小时前
从DBA到数据架构师(五):数据架构演进中的技术债务管理
数据库·程序人生·云原生·架构·dba·数据库管理员
Bruce_Liuxiaowei3 小时前
基于微步在线威胁情报的公网 IP 攻击迹象监测:threatbook_query 脚本全解析与 BruceSec 平台整合实践
数据库·tcp/ip·安全·网络安全·智能体
码农颜3 小时前
5.6.2 ⾏级锁死锁
数据库·sql·oracle
ChaHae-In4 小时前
MyBatis动态SQL与MyBatis-Plus高效开发指南
数据库·oracle·mybatis
冰暮流星4 小时前
mysql之分组查询
数据库·mysql
倒流时光三十年4 小时前
PostgreSQL Semi-Join(半连接)通俗讲解
数据库·postgresql
NiceCloud喜云5 小时前
腾讯云国际版云数据库选型:MySQL、Redis 怎么按业务架构来判断
数据库·mysql·腾讯云