news 2026/7/3 22:49:46

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NULL不是空——数据库里最反直觉的设计,90%新人踩过的坑

关键词:NULL、数据库空值、三值逻辑、NULL陷阱、SQL空值处理


大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

数据库里有一个设计,让无数新手怀疑人生——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*FROMusersWHEREphone=NULL;-- 结果:0行-- 正确写法SELECT*FROMusersWHEREphoneISNULL;

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

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

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

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

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

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

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

SELECT1+NULL;-- NULLSELECT'hello'||NULL;-- NULLSELECTNULL=0;-- UNKNOWN(不是FALSE)SELECTNULL='';-- UNKNOWN(不是FALSE)SELECTNULLANDTRUE;-- UNKNOWN(不是FALSE)

任何值和NULL运算,结果都是NULL。这在实际业务中会造成很多bug:

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

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

SELECTsalary+COALESCE(bonus,0)FROMemployees;-- NULL变成0,计算正常

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

这是最隐蔽的一个。

-- 假设子查询返回了 (1, 2, NULL)SELECT*FROMusersWHEREidNOTIN(SELECTuser_idFROMorders);-- 如果orders表里有user_id为NULL的记录,整个查询返回0行

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

WHEREid!=1ANDid!=2ANDid!=NULL

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

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

-- 方案一:NOT EXISTSSELECT*FROMusers uWHERENOTEXISTS(SELECT1FROMorders oWHEREo.user_id=u.id);-- 方案二:子查询排除NULLSELECT*FROMusersWHEREidNOTIN(SELECTuser_idFROMordersWHEREuser_idISNOTNULL);

陷阱五:排序时NULL的位置

不同数据库对NULL的排序处理不同:

SELECT*FROMusersORDERBYphoneASC;-- MySQL:NULL排在最前面-- Oracle/PostgreSQL:NULL排在最后面

如果你不确定NULL的排序行为,最好显式指定:

-- PostgreSQLSELECT*FROMusersORDERBYphoneASCNULLSFIRST;SELECT*FROMusersORDERBYphoneDESCNULLSLAST;-- MySQL(用IF/CASE处理)SELECT*FROMusersORDERBYIF(phoneISNULL,1,0),phoneASC;

NULL的正确打开方式

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

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

SELECTCOALESCE(phone,'未登记')FROMusers;SELECTsalary+COALESCE(bonus,0)FROMemployees;

聚合函数对NULL的态度

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

避免NULL的设计思路

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

CREATETABLEusers(idINTPRIMARYKEY,nameVARCHAR(50)NOTNULL,statusTINYINTNOTNULLDEFAULT0,phoneVARCHAR(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。

小耶在手,SQL不愁。

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~


参考文献

  1. SQL-92标准 - 三值逻辑规范
  2. 《SQL反模式》第12章:NULL的处理陷阱
  3. MySQL 8.0 Reference Manual - Working with NULL:https://dev.mysql.com/doc/refman/8.0/en/working-with-null.html
  4. PostgreSQL Documentation - Null Values:https://www.postgresql.org/docs/current/functions-comparison.html
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/3 22:49:04

AutoHotkey v1到v2脚本转换解决方案:现代化升级架构深度解析

AutoHotkey v1到v2脚本转换解决方案:现代化升级架构深度解析 【免费下载链接】AHK-v2-script-converter AHK v1 -> v2 script converter 项目地址: https://gitcode.com/gh_mirrors/ah/AHK-v2-script-converter AutoHotkey v1到v2脚本转换器是一款专为解决…

作者头像 李华
网站建设 2026/7/3 22:45:02

Windows平台PDF处理新选择:Poppler预编译包完全指南

Windows平台PDF处理新选择:Poppler预编译包完全指南 【免费下载链接】poppler-windows Download Poppler binaries packaged for Windows with dependencies 项目地址: https://gitcode.com/gh_mirrors/po/poppler-windows 你是否曾在Windows系统中为处理PDF…

作者头像 李华
网站建设 2026/7/3 22:35:37

DeepChem分子指纹:3种核心方法对比与实战选择指南

DeepChem分子指纹:3种核心方法对比与实战选择指南 【免费下载链接】deepchem Democratizing Deep-Learning for Drug Discovery, Quantum Chemistry, Materials Science and Biology 项目地址: https://gitcode.com/GitHub_Trending/de/deepchem 在药物发现和…

作者头像 李华
网站建设 2026/7/3 22:23:04

STM32H750XB与WSEN-ISDS的6DoF运动追踪系统设计

1. 项目背景与核心组件解析在嵌入式系统开发中,精确跟踪物体在三维空间中的运动和姿态是一个常见但极具挑战性的需求。WSEN-ISDS (2536030320001)这款MEMS传感器与STM32H750XB高性能微控制器的组合,为解决这类问题提供了理想的硬件平台。这个搭配特别适合…

作者头像 李华
网站建设 2026/7/3 22:17:38

2024年Tomcat手动配置实战与优化指南

1. 为什么2024年还需要手动配置Tomcat?在云原生和容器化大行其道的今天,很多开发者可能会有疑问:为什么还要学习传统的Tomcat手动配置?我在实际企业级项目交付中发现,至少有三个不可替代的场景:遗留系统维护…

作者头像 李华