NULL不能用=或!=比较,必须用IS NULL;替换NULL优先用IFNULL(),多层兜底才用COALESCE();WHERE中col = NULL查不到数据,因NULL = NULL返回UNKNOWN而非TRUE;IS NULL能走索引,IFNULL()性能优于COALESCE();LEFT JOIN后右表过滤须写在ON而非WHERE;NULL参与计算结果恒为NULL,需用IFNULL()显式处理。

直接说结论:NULL不能用=或!=比较,必须用IS NULL;想替换NULL值显示或参与计算,优先用IFNULL(),多层兜底才上COALESCE()。
WHERE里写col = NULL为什么永远查不到数据
因为NULL = NULL不返回TRUE,而是UNKNOWN,被WHERE当作假值过滤掉。这不是bug,是SQL标准行为。
- 错误写法:
WHERE email = NULL、WHERE status != 'active'(若status为NULL,这行也被跳过) - 正确写法:
WHERE email IS NULL、WHERE status IS NOT NULL AND status != 'active' -
IS NULL能走索引(B-tree索引支持),但IFNULL(email, '') = ''一定无法利用索引
IFNULL()和COALESCE()该选哪个
IFNULL()是MySQL原生双参数函数,语义明确、性能好;COALESCE()是标准SQL多参数函数,跨库兼容但类型推导更严格。
- 简单替换(如字段为空显示0):
SELECT IFNULL(price, 0) FROM products - 多级 fallback(比如优先用输入值,再用配置表字段,最后兜底字符串):
COALESCE(input_name, config.name, 'Guest') - 类型要一致:如果
input_name是VARCHAR,就别混写COALESCE(input_name, 123),MySQL可能报错或隐式转成字符串导致截断 - 存储过程里也适用——
SET display_name = IFNULL(tmp_name, 'Anonymous');
LEFT JOIN后WHERE过滤右表字段导致数据丢失
这是最隐蔽的坑:LEFT JOIN后右表字段为NULL是正常现象,但一旦在WHERE里加条件,整行就被过滤掉了。
- 错误写法:
SELECT * FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid'→会变成INNER JOIN效果,没订单的用户全消失 - 正确写法:把右表过滤提到
ON子句:LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'paid' - 如果真需要筛选右表为NULL的记录,用
WHERE o.id IS NULL,而不是WHERE o.status = NULL
触发器或存储过程中NULL参与计算会静默失败
只要表达式中任一操作数为NULL,整个结果就是NULL,不会报错,但后续逻辑可能崩。
- 错误写法:
SET total = price * qty;→若price或qty为NULL,total直接变NULL,插入时可能违反NOT NULL约束 - 正确做法:每个可能为NULL的字段单独清洗:
SET total = IFNULL(price, 0) * IFNULL(qty, 0); - BEFORE UPDATE触发器中判断“从非空变NULL”要用
OLD.col IS NOT NULL AND NEW.col IS NULL,不是IFNULL(OLD.col, '') != IFNULL(NEW.col, '')
NULL不是“空值”,是“缺失标记”。处理它的关键从来不是“怎么填”,而是“在哪判断、用什么语法判断、填什么才不扭曲业务含义”。尤其在触发器和JOIN场景下,一个IS NULL写错位置,整条链路的语义就偏了。


















