我最近注意到Postgres在具有唯一约束的列中处理NULL的方式不一致。
考虑一个人的表:
create table People (
pid int not null,
name text not null,
SSN text unique,
primary key (pid)
);
SSN列应保持唯一。我们可以查看:
-- Add a row.
insert into People(pid, name, SSN)
values(0, 'Bob', '123');
-- Test the unique constraint.
insert into People(pid, name, SSN)
values(1, 'Carol', '123');
第二个插入失败,因为它违反了SSN上的唯一约束。到现在为止还挺好。但是让我们尝试一下NULL:
insert into People(pid, name, SSN)
values(1, 'Carol', null);
有效。
select *
from People;
0;"Bob";"123"
1;"Carol";"<NULL>"
唯一列将为空。有趣。 Postgres如何断言null在任何方面都是唯一的,或者不是唯一的?
我想知道是否可以在唯一列中添加两行null。
insert into People(pid, name, SSN)
values(2, 'Ted', null);
select *
from People;
0;"Bob";"123"
1;"Carol";"<NULL>"
2;"Ted";"<NULL>"
是的,我可以。现在SSN列中有两行为NULL,即使SSN应该是唯一的。
Postgres documentation表示,出于唯一约束的目的,空值不被视为相等。
好。我可以看出这一点。在null处理中这是一个很好的微妙之处:通过将唯一约束列中的所有NULL都视为不相交,我们将延迟唯一约束强制执行,直到存在实际的非null值作为执行的基础。
这很酷。但这是Postgres失去我的地方。如果唯一约束列中的所有NULL都不相等,正如文档所述,那么我们应该看到选择不同查询中的所有空值。
select distinct SSN
from People;
"<NULL>"
"123"
不。那里只有一个空值。似乎Postgres有这个错误。但我想知道:还有另一种解释吗?
编辑:
Postgres文档确实指出“在此比较中,空值被视为相等”。在section on SELECT DISTINCT。虽然我不明白这个概念,但我很高兴在文档中详细说明。
答案 0 :(得分:21)
在处理null
说:
从postgres的角度来看,{p> Here是一篇关于这个主题的优秀论文。简要总结一下,根据上下文对空值进行不同的处理,并且不会对它们做出任何假设。“nulls在某种程度上就像某某某样,*所以它们应该表现得像 在这里这样的“
答案 1 :(得分:11)
最重要的是,PostgreSQL用null做了它,因为SQL标准这样说。
Nulls显然很棘手,可以用多种方式解释(未知值,缺少值等),因此当SQL标准最初编写时,作者必须在某些地方进行一些调用。我说时间已证明它们或多或少是正确的,但这并不意味着不可能有另一种数据库语言可以略微(或疯狂)地处理未知和缺失的值。但是PostgreSQL实现了SQL,就是这样。
正如在不同的答案中已经提到的那样,杰夫戴维斯写了一些关于处理空值的好文章和演示文稿。
答案 2 :(得分:5)
NULL
被认为是唯一的,因为NULL
并不代表缺少值。列中的NULL
是未知值。当你比较两个未知数时,你不知道它们是否相等,因为你不知道它们是什么。
想象一下,你有两个标有A和B的盒子。如果你没有打开盒子而你看不到里面,你永远不知道内容是什么。如果您被问到“这两个盒子的内容是否相同?”你只能回答“我不知道”。
在这种情况下,PostgreSQL会做同样的事情。当被要求比较两个NULL
时,它会说“我不知道”。这与SQL数据库中NULL
周围的疯狂语义有很大关系。链接到@JackPDouglas的文章是理解NULL
行为方式的一个很好的起点。请注意:它因供应商而异。
答案 3 :(得分:4)
唯一索引中的多个NULL值是可以的,因为x = NULL
对于所有x
都是假的,特别是当x
本身为NULL时。您还将在WHERE子句中遇到此行为,您必须在其中说WHERE x IS NULL
和WHERE x IS NOT NULL
而不是WHERE x = NULL
和WHERE x <> NULL
。