SQL LEFT JOIN在特定WHERE条件过滤下,为何会退化成INNER JOIN?
- 内容介绍
- 相关推荐
本文共计497个文字,预计阅读时间需要2分钟。
不是数据库意外修改的简单改写为:
ON 和 WHERE 的过滤时机完全不同
ON 是连接时用的,决定“哪些右表行能连上来”;WHERE 是连接后用的,决定“最终留哪些行”。关键区别在于:
-
ON t.trade_dt = d.cal_dt AND d.dim_month > '2025-07-18'→ 只让满足日期+月份条件的右表行参与连接,不满足的连不上,但左表行仍保留,d.dim_month为NULL -
WHERE d.dim_month > '2025-07-18'→ 先连完(包括NULL行),再把NULL行干掉 - 对左表字段的
WHERE(如t.trade_dt IS NOT NULL)不影响 LEFT 语义,因为它不筛右表缺失行
怎么一眼判断 LEFT 是否已退化
检查 WHERE 子句里有没有出现右表字段,且不是 IS NULL 或 IS NOT NULL 判断:
- 有
WHERE d.dim_month = '2025-07'→ 退化 - 有
WHERE d.dim_month IS NULL→ 没退化,反而专挑没匹配上的行 - 有
WHERE d.dim_month > '2025-07' OR d.dim_month IS NULL→ 没退化,但逻辑变复杂,慎用 - 只过滤左表(
WHERE t.status = 'done')→ 安全
真实场景中容易忽略的坑
动态 SQL(比如 MyBatis 的 <if>)最容易踩这个雷:前端传了 dept_name,后端拼进 WHERE,一加就变 INNER JOIN,但开发可能根本没意识到关联语义已变。更隐蔽的是复合索引失效问题——把条件从 WHERE 挪到 ON 后,如果右表没建 (cal_dt, dim_month) 这类覆盖索引,性能反而下降。所以改写前得看执行计划,不能只盯语义。
本文共计497个文字,预计阅读时间需要2分钟。
不是数据库意外修改的简单改写为:
ON 和 WHERE 的过滤时机完全不同
ON 是连接时用的,决定“哪些右表行能连上来”;WHERE 是连接后用的,决定“最终留哪些行”。关键区别在于:
-
ON t.trade_dt = d.cal_dt AND d.dim_month > '2025-07-18'→ 只让满足日期+月份条件的右表行参与连接,不满足的连不上,但左表行仍保留,d.dim_month为NULL -
WHERE d.dim_month > '2025-07-18'→ 先连完(包括NULL行),再把NULL行干掉 - 对左表字段的
WHERE(如t.trade_dt IS NOT NULL)不影响 LEFT 语义,因为它不筛右表缺失行
怎么一眼判断 LEFT 是否已退化
检查 WHERE 子句里有没有出现右表字段,且不是 IS NULL 或 IS NOT NULL 判断:
- 有
WHERE d.dim_month = '2025-07'→ 退化 - 有
WHERE d.dim_month IS NULL→ 没退化,反而专挑没匹配上的行 - 有
WHERE d.dim_month > '2025-07' OR d.dim_month IS NULL→ 没退化,但逻辑变复杂,慎用 - 只过滤左表(
WHERE t.status = 'done')→ 安全
真实场景中容易忽略的坑
动态 SQL(比如 MyBatis 的 <if>)最容易踩这个雷:前端传了 dept_name,后端拼进 WHERE,一加就变 INNER JOIN,但开发可能根本没意识到关联语义已变。更隐蔽的是复合索引失效问题——把条件从 WHERE 挪到 ON 后,如果右表没建 (cal_dt, dim_month) 这类覆盖索引,性能反而下降。所以改写前得看执行计划,不能只盯语义。

