惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
有赞技术团队
有赞技术团队
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
L
LINUX DO - 最新话题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
A
Arctic Wolf
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
小众软件
小众软件
Jina AI
Jina AI
The Cloudflare Blog
P
Palo Alto Networks Blog
AWS News Blog
AWS News Blog
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
Know Your Adversary
Know Your Adversary
T
Threat Research - Cisco Blogs
L
Lohrmann on Cybersecurity
NISL@THU
NISL@THU
G
GRAHAM CLULEY
Project Zero
Project Zero
博客园_首页
博客园 - 三生石上(FineUI控件)
罗磊的独立博客
Spread Privacy
Spread Privacy
WordPress大学
WordPress大学
Hugging Face - Blog
Hugging Face - Blog
Latest news
Latest news
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tor Project blog
S
Securelist
V
Vulnerabilities – Threatpost
T
The Exploit Database - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
IT之家
IT之家
Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Last Watchdog
The Last Watchdog
T
Tenable Blog
宝玉的分享
宝玉的分享
S
Secure Thoughts
P
Privacy & Cybersecurity Law Blog
量子位
大猫的无限游戏
大猫的无限游戏
J
Java Code Geeks
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Security Archives - TechRepublic
Security Archives - TechRepublic

博客园 - 有点懒惰的大青年

idea中查看源码 异或运算 使用Hutool对时间的花式操作 合并K个升序列表 比较器 枚举类的设计模式 spring boot中使用RedissonClient实现分布式锁 idea同时启动application,启用不同端口 @PostConstruct用法 ApplicationContext 事件发布与监听机制详解 java自带命令jps介绍 关于maven中标签的说明 mysql的跨库查询 linux命令ll显示结果的含义 Files类的使用 java环境变量设置 java值传递和引用传递 kafka的集群与可靠性 kafka的消费全流程 关于kafka 达梦数据库执行计划介绍 关于MQ 用位运算实现加减乘除(3)
用一个实际业务场景演示ON和WHERE的正确使用方式
有点懒惰的大青年 · 2026-06-29 · via 博客园 - 有点懒惰的大青年

为了让你直观理解 ONWHERE 的区别,我们构建一个经典的电商业务场景:“统计所有用户的订单情况,但只关注‘已支付’的订单,且只看‘北京’地区的用户”。

  1. 场景准备
    假设我们有两张表:
  • 用户表 (users):包含 id, name, city
  • 订单表 (orders):包含 id, user_id, amount, status ('paid', 'unpaid')

数据示例:
| users.id | users.name | users.city orders.user_id | orders.status | orders.amount |
| :--- | :--- | :--- | :--- | :--- | :--- |
| 1 | 张三 | 北京 | 1 | paid | 100 |
| 2 | 李四 | 北京 | 2 | unpaid | 200 |
| 3 | 王五 | 上海 | NULL | NULL | NULL |

业务目标:列出所有北京用户,并显示他们已支付的订单金额。如果用户没有已支付订单,金额显示为 NULL(即保留用户,但不显示未支付或无订单的数据)。


  1. 错误写法 vs 正确写法对比

❌ 错误写法:将右表过滤条件放在 WHERE 中
很多初学者会这样写,认为“反正都是过滤条件”:

SELECT 
    u.name, 
    o.amount 
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE 
    u.city = '北京'          -- 左表过滤:没问题
    AND o.status = 'paid';   -- 【陷阱】右表过滤放在 WHERE

执行逻辑与结果:

  1. JOIN:先进行左连接,生成临时表。此时李四(ID=2)会匹配到一条 status='unpaid' 的记录;王五(ID=3)匹配到 NULL。
  2. WHERE:对临时表进行全局过滤。
    • 张三:city='北京'status='paid' -> 保留。
    • 李四:city='北京'status='unpaid' -> 被剔除(因为 o.status 不为 'paid')。
    • 王五:city='上海' -> 被剔除。
    • 关键问题:如果有一个北京用户没有任何订单(o.status 为 NULL),他也会因为 NULL != 'paid' 而被 WHERE 子句剔除。
  3. 后果:这实际上把 LEFT JOIN 退化成了 INNER JOIN 的效果,丢失了“有用户但无已支付订单”的数据。如果你原本想保留李四(显示金额为 NULL),这里李四直接消失了。

✅ 正确写法:右表过滤放 ON,左表过滤放 WHERE

SELECT 
    u.name, 
    o.amount 
FROM users u
LEFT JOIN orders o 
    ON u.id = o.user_id 
    AND o.status = 'paid'   -- 【核心】右表过滤放在 ON 中
WHERE 
    u.city = '北京';         -- 【核心】左表过滤放在 WHERE 中

执行逻辑与结果:

  1. ON (连接阶段):

    • 数据库尝试将 usersorders 匹配。
    • 匹配规则是:id 相等 且 订单状态必须是 'paid'
    • 张三 (ID=1):匹配到 ID=1 的已支付订单 -> 成功连接。
    • 李四 (ID=2):虽然有 ID=2 的订单,但状态是 'unpaid',不满足 AND o.status = 'paid' -> 连接失败,右表字段补 NULL。
    • 王五 (ID=3):无订单 -> 连接失败,右表字段补 NULL。
    • 注意:此时左表(users)的所有行都还在临时表中。
  2. WHERE (过滤阶段):

    • 对连接后的临时表进行筛选。
    • 张三:city='北京' -> 保留。
    • 李四:city='北京' -> 保留(此时 o.amount 为 NULL)。
    • 王五:city='上海' -> 剔除。
  3. 最终结果:

    name amount
    张三 100
    李四 NULL

    解读:我们成功保留了李四,虽然他没有“已支付”订单,但他作为“北京用户”依然出现在列表中,符合“统计所有北京用户”的业务初衷。


  1. 核心原则总结(口诀)

为了方便记忆,请遵循以下原则:

条件类型 放置位置 原因
关联键 (如 a.id = b.id) ON 定义表之间如何“握手”。
右表过滤 (被连表) ON 在“握手”时就排除不想要的右表数据,确保左表数据不因右表不匹配而丢失。
左表过滤 (主表) WHERE 左表数据在 JOIN 后已经完整保留,最后再筛选哪些左表行需要展示。
内连接 (INNER JOIN) ON 或 WHERE 效果一样,但建议关联放 ON,过滤放 WHERE,语义更清晰。
  1. 为什么这样性能更好?
  • 提前剪枝:将 o.status = 'paid' 放在 ON 中,数据库在连接过程中就可以忽略那些“未支付”的订单行,减少参与连接的数据量,从而提升性能。
  • 避免回退:如果放在 WHERE 中,数据库可能需要先连接大量无效数据(如未支付订单),生成巨大的中间临时表,然后再通过 WHERE 丢弃它们,浪费内存和 CPU。

一句话总结:
想保留主表所有行(LEFT JOIN),过滤从表(右表)的条件必须写在 ON 里;过滤主表(左表)的条件写在 WHERE 里。

参考文章:
https://www.cnblogs.com/east7/p/16703734.html