LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

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

zhenglin
2026年7月6日 8:59 本文热度 158

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

你以为NULL代表"空"、"什么都没有"、"等于空白"。但数据库告诉你:WHERE column = NULL查不出任何数据。你以为COUNT(*)能统计所有行,但COUNT(column)却漏掉了一些。你写了一个if (value == null)的判断,结果数据死活对不上。

这一切的根源在于——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。这在实际业务中会造成很多bug:

-- 计算员工总薪资

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排在最后面

如果你不确定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值
AVG(column)忽略NULL值,且分母也不计NULL行
MAX/MIN忽略NULL值
GROUP BYNULL值被分到同一组

避免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。


阅读原文


该文章在 2026/7/6 8:59:24 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号