LEFT JOIN后WHERE过滤右表字段会导致数据丢失,因NULL参与比较结果为UNKNOWN而被过滤;正确做法是将右表条件移至ON子句。

LEFT JOIN 后右表字段为 NULL,WHERE 条件一加就丢数据
这是最隐蔽的失效场景:你写了 LEFT JOIN,本意是保留左表全部记录,但只要在 WHERE 里对右表字段加了条件(比如 WHERE b.status = 'active'),整条语句就退化成 INNER JOIN——所有右表没匹配上的行(即 b.status 为 NULL 的行)全被过滤掉。
根本原因:SQL 中 NULL = 'active' 返回 UNKNOWN,而 WHERE 只保留 TRUE 行。
- 正确做法是把右表过滤条件挪到
ON子句:LEFT JOIN b ON a.id = b.a_id AND b.status = 'active' - 如果必须在
WHERE写,得显式兼容NULL:WHERE b.status = 'active' OR b.status IS NULL - 千万别写
WHERE ISNULL(b.status, 'active') = 'active'——它让索引失效,且语义混乱
ON 条件里用 COALESCE 或 ISNULL 匹配 NULL 字段?别这么做
想“把左表的 NULL 和右表的 0 当作相等来连”,于是写 ON COALESCE(a.code, 0) = COALESCE(b.code, 0)。看起来能跑通,但实际会强制全表扫描,索引完全失效。
JOIN 性能依赖字段原始值是否可走索引,函数包装直接切断这个路径。
- 真正该做的是清洗数据:提前把业务上等价的空值标准化(如统一设为
0或'N/A'),而不是在 JOIN 时动态转换 - 若无法改源数据,可用派生表或 CTE 预处理,但要评估性能代价
-
ISNULL和COALESCE在ON里行为一致——都破坏索引,无优劣之分
SELECT 中显示 NULL 值时该选 COALESCE 还是 ISNULL?看数据库和参数数
目标很明确:把查询结果里的 NULL 替换成可读默认值,比如 'N/A' 或 0。这时函数只影响输出,不干扰执行计划,可以放心用。
- 跨库或未来可能迁移?必须用
COALESCE(b.name, 'guest')——它是 ANSI 标准,MySQL/PostgreSQL/SQL Server 全支持 - 纯 SQL Server 环境,且只补一个值?
ISNULL(b.name, 'guest')更紧凑,类型严格继承b.name,不易出隐式转换问题 - 需要链式兜底(比如优先取
name,没有就取alias,再没有才用'guest')?只能用COALESCE(b.name, b.alias, 'guest') - 注意陷阱:
COALESCE(b.name, 'guest')中若b.name是VARCHAR(10),而'guest'是 5 字符,没问题;但若写成COALESCE(b.name, 'very_long_default'),SQL Server 会按最大长度推导类型,可能意外扩大字段宽度
字符串拼接或算术运算中 NULL 导致整列变 NULL
CONCAT(a.first_name, ' ', a.last_name) 或 SUM(amount) 看似安全,但任一输入为 NULL,结果就是 NULL。这不是报错,是静默污染——报表里突然大片空白,你可能查半天才发现是某个字段漏了非空约束。
- 字符串拼接,优先用
CONCAT函数(MySQL/SQL Server 支持),它自动跳过NULL参数;否则用COALESCE(first_name, '')显式转空串 - 数值计算,一律在参与运算前兜底:
COALESCE(amount, 0)比ISNULL(amount, 0)更稳妥,尤其当amount是DECIMAL类型时,避免精度丢失 - 聚合前不处理,
AVG的分母会少计空值行,SUM虽然忽略NULL,但业务上“未填=0”就必须先转换再聚合
真正难的不是写出 COALESCE,而是判断该不该用、在哪用、用几个参数——尤其在 JOIN 条件里,一个函数就能让查询从毫秒级变成分钟级。










