别让 WHERE 把 LEFT JOIN 偷偷变成 INNER JOIN —— 一个极易忽视的 SQL 语义陷阱

在日常开发中,多表 LEFT JOIN 后再加 WHERE 过滤,看起来人畜无害,实则暗藏“语义降级”风险。以下是一条被同事踩坑的真实 SQL(表名和字段已做模糊处理):

-- 业务场景:拿主表交易日期 + 维度表月份,保留所有交易
SELECT
    t.trade_dt,            -- 主表时间戳
    d.dim_month            -- 维度表月份
FROM main_trade t
LEFT JOIN dim_date d  ON t.trade_dt = d.cal_dt
LEFT JOIN dim_date_a a ON t.trade_dt = a.cal_dt
WHERE d.dim_month > '2025-07-18'::date;

乍一看是两条 LEFT JOIN,但执行结果却把没匹配到维度表的记录全部丢光。为什么?

一、LEFT JOIN 的语义快速回顾

  • LEFT JOIN 会保留左表(main_trade)的所有行。

  • 右表未匹配到的列以 NULL 填充。

二、WHERE 对 NULL 的“绝杀”
WHERE 发生在 JOIN 之后。
一旦对右表字段写了过滤条件(d.dim_month > ...),NULL 值被排除
于是 LEFT JOIN 退化成 INNER JOIN。

步骤行数变化说明
1. LEFT JOIN主表 100 条 → 100 条(含 NULL)正常
2. WHERE d.dim_month > ...100 条 → 70 条NULL 被过滤,语义等价 INNER

三、如何保留 LEFT JOIN 语义
把对右表的过滤挪到 ON 子句 里:

SELECT
    t.trade_dt,
    d.dim_month
FROM main_trade t
LEFT JOIN dim_date d
       ON t.trade_dt = d.cal_dt
      AND d.dim_month > '2025-07-18'::date   -- 这里过滤
LEFT JOIN dim_date_a a
       ON t.trade_dt = a.cal_dt;

此时未匹配到的交易日期仍被保留,d.dim_month 为 NULL。

四、一眼判断“是否退化”的口诀
“WHERE 里出现了右表字段,且不是 IS NULL 判断 → 等价 INNER JOIN。”

五、性能小贴士

  • 若必须保留 LEFT JOIN 语义,又想在右表走索引,可在 ON 子句里加过滤,再对右表建 复合索引 (cal_dt, dim_month)

  • 如果业务可接受 INNER,则放心用 WHERE;执行计划会更简单。

六、一句话总结
LEFT JOIN 后再对右表字段做非 NULL 过滤,等同于 INNER JOIN;
要么改写成 ON 条件,要么接受语义变化,千万别被“LEFT”字样误导。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值