再看看 SQL 中的 null

NULL 与任何值的运算结果都是 NULL

NULL 有一个很重要的特性:NULL 与任何值的运算结果都是 NULL

sql 复制代码
SELECT 1 = 1; -- true
SELECT 1 = NULL; -- null
SELECT NULL = NULL; -- null
SELECT 1 != NULL; -- null
SELECT NULL != NULL; -- null

这也就意味着,对于存在列 col BOOLEAN NULLABLE 的表,如果一行中 col 列的值为 NULL,则下面的四条查询都不会包含这行

sql 复制代码
-- 等于自不必说
SELECT * FROM "table" WHERE col = true;
SELECT * FROM "table" WHERE col = false;

-- 但不等于也不会包括值为 null 的行 
SELECT * FROM "table" WHERE col != true;
SELECT * FROM "table" WHERE col != false;

IS NULL

我们其实基本都知道这点,所以我们在进行不等运算时通常会用 IS NULL / IS NOT NULL 特殊处理 NULL 列

sql 复制代码
SELECT * FROM "table" WHERE col is null OR col != true; -- 筛选 col = null 或 false
SELECT * FROM "table" WHERE col is null OR col != false; -- 筛选 col = null 或 true

IS TRUE / IS FALSE

但其实,除了 IS NULLIS 操作符后面还可以跟随 BOOL 值,那么,用 IS NOT 其实是一个更加精准的不等于操作

sql 复制代码
SELECT * FROM "table" WHERE col IS NOT true; -- 筛选 col = null 或 false
SELECT * FROM "table" WHERE col IS NOT false; -- 筛选 col = null 或 true

而对于不是 bool 类型的 nullable 列,我们则可以搭配使用 = !=IS

sql 复制代码
-- 筛选 col = null 或任何不为 1 的值
SELECT * FROM "table" WHERE col is null OR col != 1;
SELECT * FROM "table" WHERE col = 1 IS NOT TRUE; 

concat

NULL 与任何值的运算结果都是 NULL,但在函数中则不一定(取决于具体实现)

以连接字符串为例,如果中间存在 NULL,用 || 连接属于运算符,中间任何一项为 NULL 则结果为 NULL,而用 concat 连接则是函数,中间出现的 NULL 会被忽略

sql 复制代码
SELECT 'hello-' || NULL || 'world'; -- null
SELECT concat('hello-', NULL, 'world'); -- hello-world

布尔逻辑

NULL 与任何值的运算结果都是 NULL,但在布尔逻辑中则不一样...... 试试看能不能说出下面的返回值

sql 复制代码
select not NULL;
select null or null;
select null and null;

select null OR true;
select true OR null;
select null OR false;
select false OR null;

select null AND true;
select true AND null;
select null AND false;
select false AND null;

答案

sql 复制代码
select not NULL;          -- NULL
select null or null;      -- NULL
select null and null;     -- NULL

select null OR true;      -- TRUE
select true OR null;      -- TRUE
select null OR false;     -- NULL
select false OR null;     -- NULL

select null AND true;     -- NULL
select true AND null;     -- NULL
select null AND false;    -- FALSE
select false AND null;    -- FALSE

其实逻辑在于理解 NULL 代表着「未知」,与「真」「假」一起构成了一个三值集合

那么自运算,无论「未知的反面」还是「未知和未知」「未知或未知」都是未知,也因此都是 NULL

OR 的逻辑则是「有任何一个值为 TRUE 就是 TRUE」,因此,在 NULL 参与的运算中,存在 TRUE 则为 TRUE,不存在 TRUE 则为 NULL(继续未知)

AND 的逻辑则是「有任何一个值为 FALSE 就是 FALSE」,因此,在 NULL 参与的运算中,存在 FALSE 则为 FALSE,不存在 FALSE 则为 NULL(继续未知)

在聚合函数中

在聚合时,如果对 NULL 值进行聚合,它的数值是被完全忽略的 ------ 一个例子是 avg,如果对 1, 2, 3, 4, NULL 做 avg,结果是 (1+2+3+4)/4

如果期望给 NULL 在聚合时一个默认值,可以用 coalesce 函数为它赋一个「默认值」

COALESCE

上面已经提过 coalesce 函数了,再说一句就是,coalesce 函数可以接受多个参数(而不只是两个),会返回第一个非 NULL 值

例如用 coalesce(first_name, nickname, email) 取用户「昵称」

NULLIF

pg 中还存在一个 nullif 函数,接收两个值,如果两个值相等则返回 NULL 否则返回第一个值,等价于 CASE WHEN a = b THEN NULL ELSE a END

这个函数主要用来「将零值转换为空值」;举个例子就是

sql 复制代码
SELECT 
  NULLIF(TRIM(name), '') AS name,
  NULLIF(status, 'N/A')  AS status
FROM t;

还有就是搭配 NULL 的「可转换为其他类型」的特性

sql 复制代码
SELECT
  -- 将 '' 直接转换成 date 是会报错的,但是先转换为 NULL 再转换为 date 就可以了
  NULLIF(col_date, '')::date AS date
FROM t;

NULL 值在 PostgresQL 协议中的表示

虽然大概率不会引起混淆,但还是说一下,虽然我们用 psql 规则看到 NULL 值就像是文本一样,但它的底层传输是二进制,PG 规定用 len = -1 代表 NULL 值,所以不存在它与空字符串、null 字符串等混淆的情况

另外,特殊的,在 COPY 中,如果用的 text format(PG 的消息协议中,对于一个值存在 text 和 binary 两种 format),那么会用 \N 代表 NULL 值

参考:www.postgresql.org/docs/curren... 中的 Query, Parse, Bind, RowDescription, DataRow

NULL 值在文件系统中的存储

当行中有任一列值为 NULL 时,「行头」HeapTupleHeaderData 中的 t_infomask 字段内的 HEAP_HASNULL flag 会被标记为 1,此时在行头后面、其余数据前面会增加一个 null bitmap,用位图的形式存储所有列的 NULL 情况,且如果某一列的值为 null,后面的 data 中将不会出现这一列的信息

另外,null bitmap 除了用来处理 NULL 值,还会用来处理 drop column ------ 当一列被删除时其实际上依然存在着,只是后面所有的行都会带有 null bitmap 将这一列标记为 NULL(更事实上,删除列是懒删除,行内的数据都还在,只有下次更新行时才会清理这些数据 ------ 当然,清理的方式也是将它标记为 NULL);

参考:www.postgresql.org/docs/curren...

相关推荐
YMatrix 官方技术社区1 小时前
批流一体,从 Lambda 到 Domino|YMatrix 亮相 PGConf.Russia 2026,重构 PostgreSQL 极简实时架构
数据库·postgresql·重构·架构·ymatrix
腥辣甜咸2 小时前
想用队列?但我一个中间件都不想引入:PGMQ 超简单使用
postgresql
hudson20222 小时前
work_mem: 这是一个陷阱!
后端·postgresql
李长渊哦3 小时前
家用宽带动态公网 IP 下 Node + PostgreSQL 服务的 DDNS 全流程部署实践
网络协议·tcp/ip·postgresql
程序人生5183 小时前
ubuntu桌面版安装后配置允许root通过图形界面登录和ssh登录 亲测有效
ubuntu·postgresql·ssh
lbb 小魔仙3 小时前
无公网 IP 环境下的 PostgreSQL 远程访问方案:基于内网穿透技术的全流程解析
网络协议·tcp/ip·postgresql
蓦然乍醒1 天前
使用 DBeaver 还原 PostgreSQL 备份文件 (.bak) 技术文档
数据库·postgresql
l1t1 天前
DeepSeek总结的PostgreSQL使用 RDTSC 降低 EXPLAIN ANALYZE 的计时开销
数据库·postgresql
l1t1 天前
DeepSeek总结的PostgreSQL检查点和写入风暴
jvm·postgresql·oracle
丸辣,我代码炸了1 天前
用 PostgreSQL 一库模拟 MySQL / MongoDB / Redis / Elasticsearch(附 ts_rank 详解)
mysql·mongodb·postgresql