sql中判断null必须用is null而非= null,因null表示缺失而非值;coalesce仅用于显示兜底,不改变where过滤逻辑;group by和order by中null被统一处理;应用层需用可空类型映射数据库null字段。

WHERE条件里判断NULL不能用等号
SQL里NULL不是值,是“缺失”的标记,所以= NULL永远返回UNKNOWN,不会命中任何行。你写WHERE status = NULL,结果一定是空的,哪怕表里真有NULL。
必须用IS NULL或IS NOT NULL:
SELECT * FROM users WHERE email IS NULL;
- 别用
WHERE col = NULL或WHERE col != NULL -
IS NULL能走索引(取决于数据库和字段是否允许NULL) - 在JOIN条件里也一样——
ON a.id = b.user_id不会匹配b.user_id为NULL的行,得显式写OR b.user_id IS NULL才可能覆盖
COALESCE只解决显示/计算时的NULL替代
COALESCE是按顺序取第一个非NULL的值,常用来兜底,但它不改变原数据,也不影响WHERE过滤逻辑。
比如想把空邮箱显示成"(not set)":
SELECT name, COALESCE(email, '(not set)') AS email_display FROM users;
- 参数类型要兼容,
COALESCE(1, 'abc')在多数数据库会报错或隐式转换(行为不一致) - 第一个参数为
NULL时,才会继续看第二个;如果所有参数都是NULL,结果仍是NULL - 别在WHERE里滥用:
WHERE COALESCE(email, '') = ''看似能抓到NULL和空字符串,但可能无法走索引,且语义模糊——你到底要的是“没填”,还是“填了空字符串”?建议拆开写:email IS NULL OR email = ''
GROUP BY和ORDER BY里NULL的排序与分组行为
不同数据库对NULL在GROUP BY和ORDER BY中的处理一致:所有NULL被当作相同值分到一组,排序时默认排在最前(PostgreSQL)或最后(MySQL 8.0+),但不是绝对的。
- MySQL旧版本(5.7)中
ORDER BY col ASC把NULL排最前;新版本可加NULLS FIRST或NULLS LAST(标准SQL语法,但MySQL暂不支持,PostgreSQL支持) -
GROUP BY nullable_col会让所有NULL归为同一组,COUNT(nullable_col)会跳过NULL,而COUNT(*)不会 - 如果业务上需要区分“明确为空字符串”和“未填写(NULL)”,建表时就该避免让两者共存——要么全用
NOT NULL加默认值,要么用独立字段标记状态
应用层读取时NULL引发的NPE或类型错误
ORM(如MyBatis、Hibernate)或直连驱动(如pgx、mysql2)从数据库取回NULL后,若字段映射到非可空类型(如Java的int、Go的int),就会抛异常或静默截断。这是线上最常见的NULL相关故障点。
- 数据库字段允许NULL → 应用层变量必须声明为可空类型(如Java的
Integer、Go的*int、TypeScript的string | null) - 用
COALESCE兜底不如在应用层做防御性检查,因为SQL兜底掩盖了数据质量问题;而应用层判空能触发告警或埋点 - 尤其注意时间类型:
NULL转time.Time在Go里会变成零值0001-01-01,不是错误,但后续比较逻辑全乱
实际查NULL从来不是技术难点,难的是所有人对“这个字段什么时候该是NULL、什么时候该是空字符串、要不要默认值”没共识。表结构定下来那一刻,NULL的语义就该写进注释里,而不是靠每个查询都加一遍IS NULL去猜。










