PostgreSQL15改进了UNIQUE和NULL
最近发布了PG15 beta 2。本文关注对有NULL值的列进行UNIQUE约束的改进。虽然唯一约束的细小差别不如加速排序那样惊艳,但对于提高数据库开发人员对数据质量的控制来说,总归是一个好处。
邮件列表对此进行了讨论:
https://www.postgresql.org/message-id/flat/84e5ee1b-387e-9a54-c326-9082674bde78@enterprisedb.com
PG15的release notes中指出:“允许唯一约束和索引将NULL值看作不同的值。以前NULL值总是被索引认为是不同的值,但现在可以通过使用UNIQUE NULLS NOT DISTINCT创建约束和索引来改变。”
UNIQUE的两种风格
创建2个表来了解这方面的意义。null_old_style表有两个列(val1,val2)上的UNIQUE约束。val2允许NULL值。
代码语言:javascript复制CREATE TABLE null_old_style
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
val1 TEXT NOT NULL,
val2 TEXT NULL,
CONSTRAINT uq_val1_val2
UNIQUE (val1, val2)
);
null_new_style表使用新的选项:UNIQUE NULLS NOT DISTINCT。和上面的表唯一区别就是唯一约束的新语法:
代码语言:javascript复制CREATE TABLE null_new_style
(
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
val1 TEXT NOT NULL,
val2 TEXT NULL,
CONSTRAINT uq_val1_val2_new
UNIQUE NULLS NOT DISTINCT (val1, val2)
);
允许插入数据的变化
PG14及之前版本,唯一约束将NULL值看作和其他NULL值不相等,PG14手册“当索引声明为唯一时,不允许具有相同索引值的多个行。NULL值视为不相等”。
这与SQL标准的处理方式是一致的。一般情况下,NULL是未知的,不可能确定一个未知数是否等于另一个未知数,并没有违反UNIQUE约束。向null_old_style表插入5行:
代码语言:javascript复制INSERT INTO null_old_style (val1, val2)
SELECT 'Hello', NULL
FROM generate_series(1, 5)
;
SELECT * FROM null_old_style;
id|val1 |val2|
-- ----- ----
1|Hello| |
2|Hello| |
3|Hello| |
4|Hello| |
5|Hello| |
这个行为是ANSI SQL标准。但我并不喜欢,因为不够严格。
使用新选项NULLS NOT DISTINCT,唯一约束不允许重复NULL值:
代码语言:javascript复制INSERT INTO null_new_style (val1, val2)
SELECT 'Hello', NULL;
SELECT * FROM null_new_style;
id|val1 |val2|
-- ----- ----
1|Hello| |
尝试再插入一行:
代码语言:javascript复制INSERT INTO null_new_style (val1, val2)
SELECT 'Hello', NULL;
SQL Error [23505]: ERROR: duplicate key value violates unique constraint "uq_val1_val2_new"
Detail: Key (val1, val2)=(Hello, null) already exists.
当然,将val1改变一下,就可以插入了:
代码语言:javascript复制INSERT INTO null_new_style (val1, val2)
SELECT 'World', NULL;
id|val1 |val2|
-- ----- ----
1|Hello| |
3|World| |
这样就符合我心里预期了。
总结
很高兴看到PG15中新增UNIQUE NULLS NOT DISTINCT语法。增加了数据指令控制级别。这也是一个影响较低的更新,默认操作照常使用。
原文
https://blog.rustprooflabs.com/2022/07/postgres-15-unique-improvement-with-null