
COALESCE 和 IFNULL 到底该用哪个
MySQL 里处理 NULL 最常用的两个函数是 COALESCE 和 IFNULL,但它们行为不同、兼容性也不同,不能随便换着用。
COALESCE 是 SQL 标准函数,支持任意多个参数,从左到右返回第一个非 NULL 值;IFNULL 是 MySQL 特有函数,只接受两个参数,等价于 IF(expr1 IS NOT NULL, expr1, expr2)。
- 如果要兼容 PostgreSQL 或写可移植 SQL,必须用
COALESCE - 如果明确只跑在 MySQL 且逻辑简单(比如“字段为空就填默认值”),
IFNULL更直白,性能略好(少一层参数解析) -
COALESCE(NULL, NULL, 'a', 'b')返回'a';IFNULL(NULL, IFNULL(NULL, 'a'))才等效——嵌套太深容易写错
WHERE 条件里直接比较 NULL 会失效
这是新手最常踩的坑:WHERE col = NULL 永远不成立,因为 NULL 不能用 = 判断,必须用 IS NULL 或 IS NOT NULL。
但很多人想“把 NULL 当成 0 处理再比较”,于是写成 WHERE IFNULL(col, 0) > 10。这看似可行,实际会**让索引失效**——只要对列用了函数,MySQL 就无法走索引范围扫描。
- 正确做法是拆开:
WHERE (col > 10) OR (col IS NULL AND 0 > 10)(当然这里第二部分恒假,可省) - 更常见的是补全业务逻辑:比如“未填写价格的记录不参与筛选”,那就该写
WHERE col IS NOT NULL AND col > 10 - 如果真要统一转义,建议在应用层或视图里做,别塞进 WHERE
GROUP BY + 聚合函数遇到 NULL 的隐式分组
NULL 在 GROUP BY 中会被当作一个独立分组值,这点和空字符串 '' 完全不同。比如 SELECT category, COUNT(*) FROM t GROUP BY category,所有 category IS NULL 的行会挤在同一个分组里。
问题在于:你可能以为 COALESCE(category, 'unknown') 能“修复”它,但要注意类型一致性——如果 category 是 VARCHAR(10),而 'unknown' 长度超限,MySQL 5.7+ 会截断并报 warning(严格模式下直接报错)。
- 检查字段长度:
SHOW COLUMNS FROM t LIKE 'category' - 稳妥写法:
COALESCE(category, SUBSTRING('unknown', 1, CHARACTER_MAXIMUM_LENGTH))(需先查元数据) - 更推荐在 INSERT/UPDATE 时就约束业务逻辑,避免 NULL 进入关键分组字段
JSON 函数返回 NULL 的连锁反应
MySQL 5.7+ 的 JSON_EXTRACT、JSON_UNQUOTE 等函数,只要路径不存在或类型不匹配,一律返回 NULL。这时候再套 IFNULL 看似安全,但容易掩盖真实问题。
例如:SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(data, '$.name')), 'N/A') FROM t —— 如果 data 是损坏 JSON(如 '{ "name": '),JSON_EXTRACT 返回 NULL,但你根本不知道是路径错还是数据坏。
- 先用
JSON_VALID(data)排查数据质量 - 用
JSON_CONTAINS_PATH(data, 'one', '$.name')显式判断路径是否存在 - 避免多层嵌套
IFNULL(IFNULL(...)),改用COALESCE并加注释说明每一层 fallback 的语义
NULL 不是值,是“缺失”的标记;所有对它的处理本质上都是在定义业务规则。函数只是工具,真正容易被忽略的,是那个没写进 SQL 的需求文档里的“NULL 到底代表什么”。










