由 John Doe 八月 11, 2025
你需要防止在唯一索引中插入带有空值 NULL 的多条记录吗?现在,PostgreSQL 添加了唯一约束的 NULL 处理选项。

特性提交日志
添加唯一约束的 NULL 处理选项。
SQL 标准对于唯一约束中的 NULL 值应被视为相等还是不相等一直存在歧义。不同的数据库实现有着不同的行为。在 SQL:202x 草案中,这一问题得到了规范化:将其定义为依赖具体实现,并在唯一约束定义中添加了 UNIQUE [ NULLS [NOT] DISTINCT ] 选项,允许显式选择行为。
本次提交为 PostgreSQL 新增了这一选项。默认行为仍为UNIQUE NULLS DISTINCT(即将 NULL 视为不同)。在 B 树索引中实现这一点相当简单;提交的大部分内容只是将标识传递到所有需要它的地方。
CREATE UNIQUE INDEX的语法扩展并非来自 SQL 标准,而是 PostgreSQL 特有的。
讨论:https://www.postgresql.org/message-id/flat/84e5ee1b-387e-9a54-c326-9082674bde78@enterprisedb.com
示例
PostgreSQL 新版本添加了 UNIQUE 空值处理选项。这是什么功能呢?让我们来通过一个小示例,展示下这项新功能的效果。
考虑以下表和唯一索引:
create table t ( a int );
create unique index i on t ( a );
显然,这会阻止我们多次添加相同的值:
insert into t values (1);
insert into t values (1);
ERROR: duplicate key value violates unique constraint "i"
DETAIL: Key (a)=(1) already exists.
但是对于 NULL 呢?NULL 表示未定义,那么当你尝试插入多行 NULL 值时,唯一索引应该如何处理呢?两个 NULL 值是相同的还是不同的?如果你问 PostgreSQL NULL 是否等于 NULL,得到的结果是未定义(或 NULL):
select null = null;
?column?
----------
(1 row)
将未定义的东西与另一个未定义的东西进行比较没有多大意义。我们上面创建的唯一索引的行为正是如此:
insert into t values(null);
insert into t values(null);
insert into t values(null);
insert into t values(null);
insert into t values(null);
PostgreSQL 新版本会为你提供选择:
create table tt ( a int );
create unique index ii on tt (a) nulls not distinct;
insert into tt values(null);
insert into tt values(null);
ERROR: duplicate key value violates unique constraint "ii"
DETAIL: Key (a)=(null) already exists.
当然,默认行为与我们现在的行为一致,但也可以显式指定:
create unique index iii on tt (a) nulls distinct;
非常不错的体验。感谢社区的所有相关人员。
参考
提交日志:https://git.postgresql.org/pg/commitdiff/94aa7cc5f707712f592885995a28e018c7c80488