首页 > 数据库 >NULL不是空——数据库最反直觉设计,90%新人踩坑

NULL不是空——数据库最反直觉设计,90%新人踩坑

来源:互联网 2026-07-08 08:38:11

NULL是数据库中表示“未知”的特殊标记,而非空值或0。它引入三值逻辑,导致用=NULL查不出数据、COUNT(column)忽略NULL、运算结果全为NULL、NOTIN遇NULL返回空、排序位置因数据库而异。正确处理需用ISNULL判断、COALESCE赋默认值、NOTEXISTS替代NOTIN,建表时尽量设置NOTNULL。

数据库里有个设计,让无数新手在深夜怀疑人生——NULL。

你可能以为NULL就是空、就是什么都没有、就是空白。但数据库爸爸会告诉你:WHERE column = NULL?不好意思,一条数据都查不出来。你以为COUNT(*)能统计所有行,结果COUNT(column)悄悄漏掉了一堆。你写了个if(value == null)的判断,数据死活对不上——道理都懂,可它就是跟你作对。

长期稳定更新的攒劲资源: >>>点此立即查看<<<

一切的根源在于——NULL在数据库里,压根不是空值,而是一个特殊标记,表示“未知”或“不适用”。

今天就把它彻底讲清楚,帮你把这些反直觉的坑一个个填上。


几个先搞明白的概念

NULL的本质:NULL不是空字符串'',不是0,不是false。它是一个独立的特殊值,表示“这个字段当前没有确定的值”。你可以把它想象成一张问卷上的“未作答”——不是“否”,也不是“空白”,而是“我不知道”。

三值逻辑:大多数编程语言只有TRUE和FALSE两种结果。但NULL一掺和进来,逻辑运算就变成了三值:TRUE、FALSE、UNKNOWN。涉及NULL时,结果很可能就是UNKNOWN。而WHERE条件只返回TRUE的行,UNKNOWN被当作FALSE处理,这就是为什么很多查询“查不出数据”的根本原因。

空字符串 vs NULL:空字符串''是一个确定的值——它就是一个长度为0的字符串。NULL表示“没有值”。''和NULL在数据库里是两码事,千万别混为一谈。


NULL的五大反直觉陷阱

陷阱一:WHERE column = NULL 查不出任何数据

新手必踩,没跑。

-- 你以为这样能查出所有phone为空的记录
SELECT * FROM users WHERE phone = NULL;
-- 结果:0行

-- 正确写法
SELECT * FROM users WHERE phone IS NULL;

为什么?因为NULL不等于任何东西,包括它自己。在数据库中:

NULL = NULL  →  UNKNOWN(不是TRUE)
NULL != NULL →  UNKNOWN(不是TRUE)

NULL = NULL的结果是UNKNOWN,而WHERE只认TRUE,所以查出来0行。必须用IS NULLIS NOT NULL来判断。

陷阱二:COUNT(column) 忽略NULL值

-- 假设users表有100行,其中10行phone为NULL
SELECT COUNT(*) FROM users;     -- 100(统计所有行)
SELECT COUNT(phone) FROM users; -- 90(忽略NULL值)

COUNT(*)统计行数,不管字段是什么。COUNT(column)只看该字段非NULL的行数。如果你想知道“有多少人有手机号”,用COUNT(phone);如果你想知道“总共有多少人”,用COUNT(*)。

陷阱三:NULL参与运算,结果还是NULL

SELECT 1 + NULL;        -- NULL
SELECT 'hello' || NULL; -- NULL
SELECT NULL = 0;        -- UNKNOWN(不是FALSE)
SELECT NULL = '';       -- UNKNOWN(不是FALSE)
SELECT NULL AND TRUE;   -- UNKNOWN(不是FALSE)

任何值和NULL运算,结果都是NULL。这在业务代码里会埋下不少隐形冲击波:

-- 计算员工总薪资
SELECT salary + bonus FROM employees;
-- 如果某个员工bonus是NULL,整条记录的总薪资就是NULL

正确做法是用COALESCE把NULL替换为默认值:

SELECT salary + COALESCE(bonus, 0) FROM employees;
-- NULL变成0,计算正常

陷阱四:NOT IN 遇到NULL,整个查询结果为空

这个最隐蔽,杀伤力也最大。

-- 假设子查询返回了 (1, 2, NULL)
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders);

-- 如果orders表里有user_id为NULL的记录,整个查询返回0行

为什么?因为NOT IN在底层展开成:

WHERE id != 1 AND id != 2 AND id != NULL

id != NULL的结果是UNKNOWN,整个AND表达式变成UNKNOWN,WHERE就不返回任何行了。

解决办法:用NOT EXISTS替代NOT IN,或者在子查询中排除NULL:

-- 方案一:NOT EXISTS
SELECT * FROM users u WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.user_id = u.id
);

-- 方案二:子查询排除NULL
SELECT * FROM users WHERE id NOT IN (
    SELECT user_id FROM orders WHERE user_id IS NOT NULL
);

陷阱五:排序时NULL的位置

不同数据库对NULL的处理方式不一样:

SELECT * FROM users ORDER BY phone ASC;
-- MySQL:NULL排在最前面
-- Oracle/PostgreSQL:NULL排在最后面

如果不确定数据库的默认行为,最好显式指定:

-- PostgreSQL
SELECT * FROM users ORDER BY phone ASC NULLS FIRST;
SELECT * FROM users ORDER BY phone DESC NULLS LAST;

-- MySQL(用IF/CASE处理)
SELECT * FROM users ORDER BY IF(phone IS NULL, 1, 0), phone ASC;

NULL的正确打开方式

判断NULL:用IS NULLIS NOT NULL,别用=!=

处理NULL参与计算:用COALESCE提供默认值。

SELECT COALESCE(phone, '未登记') FROM users;
SELECT salary + COALESCE(bonus, 0) FROM employees;

聚合函数对NULL的态度

函数 对NULL的处理
COUNT(*) 统计所有行,不忽略NULL
COUNT(column) 忽略该列的NULL值
SUM(column) 忽略NULL值
A VG(column) 忽略NULL值,且分母也不计NULL行
MAX/MIN 忽略NULL值
GROUP BY NULL值被分到同一组

避免NULL的设计思路

如果业务上某个字段“必须有值”,建表时就加上NOT NULL约束,并设置默认值:

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    status TINYINT NOT NULL DEFAULT 0,
    phone VARCHAR(20)  -- 允许NULL,因为确实可能没登记
);

NOT NULL的字段就别留NULL。NULL存在的每一处,都是未来查询时可能踩的坑。


总结

NULL不是空值,是“未知”。记住三句话:

  1. 判断NULL用IS NULL,别用=
  2. NULL参与运算结果是NULL,用COALESCE处理
  3. NOT IN遇到NULL会吞掉所有结果,改用NOT EXISTS

建表时能NOT NULL就别留NULL,少一个NULL,少十个bug。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备2026025700号-3 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。