SQL LEFT JOIN 与 WHERE 条件的隐藏坑

在日常开发中,我们经常用 LEFT JOIN 来保留左表数据,即使右表没有匹配记录也能查到结果。然而,如果不小心把右表的过滤条件写在了 WHERE 子句里,就会让 LEFT JOIN 失效,变成了"变相 INNER JOIN"。

本文通过一个真实案例,分析这个坑的成因、表现和正确写法。


1. 场景背景

我在调试一个查询时,遇到一个很奇怪的现象:

  • 单独查右表子查询是有数据的
  • 但是和左表 LEFT JOIN 之后,结果却是空的

SQL 如下:

sql 复制代码
SELECT
    b.project_code,
    b.account_code,
    b.group_code,
    b.group_name,
    p.finish_date,
    p.actual_finish_date,
    p.job_code
FROM (
    -- 楼栋表,按项目、账户、组团去重
    SELECT DISTINCT
        project_code,
        account_code,
        group_code,
        group_name
    FROM t_jsc_ysjgzj_building_account 
    WHERE batch_number = (SELECT max(batch_number) FROM t_jsc_ysjgzj_building_account)
        AND delete_flag = 0
        AND group_code IS NOT NULL 
        AND group_code != ''
        AND account_code = '0200096829000128958'
) b
LEFT JOIN t_jh_all_plan_list_detail p
    ON b.group_code = p.gr_id
WHERE p.delete_flag = 0
  AND p.is_ignored = 0
  AND p.job_code IN ('JT00056', 'JT00019');

2. 问题原因

很多人误以为 LEFT JOIN 后写任何条件都能保留左表数据,其实不然。

右表无匹配行 时,右表的所有列都是 NULL

  • 条件 p.delete_flag = 0 等价于 NULL = 0,结果是 FALSE
  • 条件 p.job_code IN (...) 等价于 NULL IN (...),结果也是 FALSE

于是,这些记录在 WHERE 阶段被过滤掉,相当于把 LEFT JOIN 转成了 INNER JOIN


3. 直观对比

写法位置 右表无匹配行时 左表数据是否保留
条件写在 ON 条件判断发生在匹配阶段,右表为 NULL 依然保留左表 ✅ 保留
条件写在 WHERE 条件判断在匹配后过滤阶段,右表 NULL 导致条件不成立 ❌ 不保留

4. 正确写法

将对右表的过滤条件移动到 ON 子句中:

sql 复制代码
SELECT
    b.project_code,
    b.account_code,
    b.group_code,
    b.group_name,
    p.finish_date,
    p.actual_finish_date,
    p.job_code
FROM (
    SELECT DISTINCT
        project_code,
        account_code,
        group_code,
        group_name
    FROM t_jsc_ysjgzj_building_account 
    WHERE batch_number = (SELECT max(batch_number) FROM t_jsc_ysjgzj_building_account)
        AND delete_flag = 0
        AND group_code IS NOT NULL 
        AND group_code != ''
        AND account_code = '0200096829000128958'
) b
LEFT JOIN t_jh_all_plan_list_detail p
    ON b.group_code = p.gr_id
    AND p.delete_flag = 0
    AND p.is_ignored = 0
    AND p.job_code IN ('JT00056', 'JT00019');

这样做的好处:

  • 保证了 LEFT JOIN 的"左表优先保留"特性
  • 避免了无匹配数据被误删

5. 总结

  • LEFT JOIN 条件要分清位置

    • 右表条件 → 写在 ON(保留左表数据)
    • 左表条件 → 写在 WHERE(正常过滤)
  • 检查 SQL 结果异常时

    • 看右表字段是否在 WHERE 中被硬性约束
    • 如果是,就考虑挪到 ON
  • 口诀

    复制代码
    LEFT JOIN 不生效,
    多半 WHERE 害的。
    条件放在 ON,
    左表才安全。
    ``
相关推荐
专注API从业者3 小时前
Open‑Claw 实战|无需逆向,快速搭建电商商品监控与数据分析系统
开发语言·数据结构·数据库·数据分析·php
2301_800074213 小时前
MySQL部分语句
数据库·mysql
CodeStats3 小时前
MySQL与Oracle数据类型底层设计原理对比(下)
数据库·mysql·oracle
疯狂打码的少年4 小时前
【数据库技术】SQL高级查询(分组/排序/聚合函数)
java·数据库·笔记·sql
IvorySQL4 小时前
PostgreSQL 日报|备用服务器 FSM 数据不一致(9 月 2 日)
服务器·数据库·postgresql
zcmodeltech4 小时前
风力发电沙盘模型控制系统设计与灯光联动实现方案——基于STM32与Modbus RTU,服务范围覆盖全国的多场景风力发电沙盘模型定制
网络·数据库·stm32·单片机·嵌入式硬件·能源
程序员黎剑4 小时前
MySQL-JOIN优化-NLJ与BNL的区别与实战
数据库·mysql
Java小白笔记4 小时前
MyBatis XML Mapper 标签与 CRUD 原理实战
xml·数据库·mybatis
JMchen1234 小时前
【Android 性能优化实战 60 讲】03 Perfetto 现代分析利器:SQL 查询 Trace 数据,微秒级定位函数 CPU 耗时
android·sql·性能优化·实战·源码分析·perfetto·启动优化
JavaPub-rodert5 小时前
一个后台系统如何同时支持 MySQL、PostgreSQL、SQLite、SQL Server?从 ShiyuAdmin 的 GORM 多数据库适配说起
数据库·mysql·postgresql