什么是 PostgreSQL NULL 值?
在数据库的世界里,数据不是“有”就是“没有”,但“没有”这件事,其实并不简单。你可能以为“空值”就是什么都没有,但在 PostgreSQL 中,这种“空”是有特定含义的——它就是 NULL。
NULL 并不是“空字符串”('')、也不是“0”或“false”,它代表的是“未知”或“缺失”的数据。你可以把它想象成一个未填写的表格字段:比如你在填写一份个人信息表,名字那栏空白,不是因为你不叫名字,而是你暂时不知道,或者没写。这个“不知道”在数据库里,就是 NULL。
在 PostgreSQL 中,NULL 是一种特殊的占位符,表示某个字段的值尚未确定。它不像普通值那样可以参与运算或比较,而是有自己独特的规则。理解这一点,是掌握 PostgreSQL 数据处理的基础。
NULL 值的三大特性
不等于任何值,包括它自己
这是最容易让人踩坑的一点:NULL 不等于 NULL。
-- 错误的写法:你以为这能查出所有 NULL 的数据
SELECT * FROM users WHERE age = NULL;
-- 正确的做法:必须使用 IS NULL
SELECT * FROM users WHERE age IS NULL;
💡 注释:在 SQL 中,
=运算符无法判断NULL,因为NULL表示“未知”,和“未知”比,结果也是“未知”。所以必须用IS NULL或IS NOT NULL来判断。
与 NULL 的运算结果仍是 NULL
当你对一个 NULL 值进行算术、字符串拼接等操作时,结果依然是 NULL。
-- 示例:计算年龄加 10
SELECT age + 10 AS new_age FROM users;
-- 如果 age 是 NULL,结果就是 NULL,不会变成 10 或其他数字
💡 注释:这就像你手里的一个盒子,里面什么都没有。你把它和另一个盒子叠在一起,但你不知道里面有没有东西,最终结果也只能是“未知”——这就是
NULL的“传染性”。
比较运算符无法判断 NULL
你不能用 =, <>, >, < 来判断 NULL,因为这些操作的结果是 UNKNOWN,而不是 TRUE 或 FALSE。
-- 错误示例
SELECT * FROM products WHERE price = NULL;
-- 正确写法
SELECT * FROM products WHERE price IS NULL;
💡 注释:在 SQL 的三值逻辑中,布尔结果只有三种:
TRUE、FALSE、UNKNOWN。NULL比较的结果是UNKNOWN,所以无法用于WHERE子句的条件判断。
如何正确处理 PostgreSQL NULL 值
使用 IS NULL 和 IS NOT NULL
这是最常用、最安全的方式判断 NULL。
-- 查找所有没有填写手机号的用户
SELECT name, phone FROM users WHERE phone IS NULL;
-- 查找所有填写了手机号的用户
SELECT name, phone FROM users WHERE phone IS NOT NULL;
💡 注释:
IS NULL是标准 SQL 的语法,几乎所有数据库都支持,包括 PostgreSQL。记住,它是一个操作符,不是函数。
使用 COALESCE 函数处理默认值
当你希望在 NULL 时提供一个默认值,可以用 COALESCE。
-- 如果 phone 是 NULL,就显示 '未提供'
SELECT name, COALESCE(phone, '未提供') AS contact FROM users;
💡 注释:
COALESCE接受多个参数,返回第一个非NULL的值。它非常适用于字段值缺失时的优雅降级。
使用 NULLIF 避免无效数据
当你想把某个值转换为 NULL,比如避免除以零,可以用 NULLIF。
-- 避免除以零:如果 denominator 是 0,则返回 NULL
SELECT numerator / NULLIF(denominator, 0) AS result FROM calculations;
💡 注释:
NULLIF(a, b)的作用是:如果a = b,就返回NULL,否则返回a。这在数据清洗中特别实用。
实际案例:用户信息表中的 NULL 处理
假设我们有一个用户表 users,结构如下:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100),
phone VARCHAR(20),
age INT,
created_at TIMESTAMP DEFAULT NOW()
);
我们插入一些测试数据:
INSERT INTO users (name, email, phone, age) VALUES
('张三', 'zhangsan@example.com', '13800138000', 25),
('李四', NULL, '13900139000', NULL),
('王五', 'wangwu@example.com', NULL, 30),
('赵六', NULL, NULL, 28);
现在我们来查询这些数据:
-- 查看所有记录
SELECT * FROM users;
-- 输出结果:
-- id | name | email | phone | age | created_at
-- 1 | 张三 | zhangsan@example.com | 13800138000 | 25 | ...
-- 2 | 李四 | (null) | 13900139000 | (null) | ...
-- 3 | 王五 | wangwu@example.com | (null) | 30 | ...
-- 4 | 赵六 | (null) | (null) | 28 | ...
案例 1:查找未填写邮箱的用户
SELECT name, email FROM users WHERE email IS NULL;
💡 注释:这条语句会返回“李四”和“赵六”,因为他们没有填写邮箱。
案例 2:为缺失电话的用户补上默认值
SELECT name, COALESCE(phone, '暂无电话') AS contact FROM users;
💡 注释:输出中,“王五”和“赵六”的电话会显示为“暂无电话”,而其他人显示真实号码。
案例 3:统计年龄为空的用户比例
SELECT
COUNT(*) AS total_users,
COUNT(age) AS filled_age,
COUNT(age) * 100.0 / COUNT(*) AS percentage_filled
FROM users;
💡 注释:
COUNT(age)只统计非NULL的值,所以这里可以计算出“年龄填写完整率”。
NULL 值的常见陷阱与最佳实践
陷阱 1:误用 = 判断 NULL
-- ❌ 错误:不会返回任何结果
SELECT * FROM users WHERE age = NULL;
-- ✅ 正确:使用 IS NULL
SELECT * FROM users WHERE age IS NULL;
陷阱 2:在索引中使用 NULL
虽然 PostgreSQL 允许在列上创建索引,但 NULL 值不会被索引包含(除非是部分索引)。
-- 创建索引时,NULL 值不会被存储
CREATE INDEX idx_users_age ON users(age);
💡 建议:如果需要快速查询
NULL值,可以考虑使用部分索引:
CREATE INDEX idx_users_age_null ON users(age) WHERE age IS NULL;
陷阱 3:聚合函数忽略 NULL
SUM、AVG、COUNT 等聚合函数默认忽略 NULL。
SELECT AVG(age) FROM users; -- 只计算非 NULL 的 age
💡 注释:
COUNT(*)会统计所有行,而COUNT(age)只统计非空的 age。
如何设计数据库以减少 NULL 值的困扰?
1. 合理使用 NOT NULL 约束
在字段确实必须有值时,使用 NOT NULL。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL, -- 必须填写
email VARCHAR(100) UNIQUE, -- 可以为空,但唯一
phone VARCHAR(20),
age INT
);
💡 注释:
NOT NULL能避免意外的NULL值,提升数据完整性。
2. 使用默认值代替 NULL
对于“未填写”但需要默认行为的字段,设置默认值。
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) DEFAULT 0.00,
status VARCHAR(20) DEFAULT 'active'
);
💡 注释:
DEFAULT保证即使没有插入值,也能有合理默认。
总结:PostgreSQL NULL 值的正确打开方式
NULL 不是“空”,也不是“0”,它是“未知”。它在 PostgreSQL 中扮演着关键角色,但处理不当会引发严重错误。
- 用
IS NULL和IS NOT NULL判断NULL - 用
COALESCE提供默认值 - 用
NULLIF避免无效计算 - 聚合函数自动忽略
NULL - 合理设计表结构,减少
NULL的使用
掌握这些技巧,你就能在面对复杂的业务数据时,从容应对各种 NULL 场景。记住:在处理 PostgreSQL NULL 值 时,不是“它是不是空”,而是“它为什么是空”。理解背后的原因,才能写出更健壮的 SQL 代码。
最后,别忘了:NULL 不是错误,它是数据世界中的一种“沉默的语言”。读懂它,你就是数据库的真正主人。