为什么Postgres在涉及唯一约束的情况下处理NULL不一致?

Ala*_*lan 19 postgresql null unique

我最近注意到Postgres在具有唯一约束的列中处理NULL的方式不一致.

考虑一个人的表:

create table People (
   pid  int  not null,
   name text not null,
   SSN  text unique,
 primary key (pid)
);
Run Code Online (Sandbox Code Playgroud)

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');
Run Code Online (Sandbox Code Playgroud)

第二个插入失败,因为它违反了SSN上的唯一约束.到现在为止还挺好.但是让我们尝试一下NULL:

insert into People(pid, name, SSN)
values(1, 'Carol', null);
Run Code Online (Sandbox Code Playgroud)

这样可行.

select *    
from People;

0;"Bob";"123"
1;"Carol";"<NULL>"
Run Code Online (Sandbox Code Playgroud)

唯一列将为空.有趣.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>"
Run Code Online (Sandbox Code Playgroud)

我可以.现在SSN列中有两行NULL,即使SSN应该是唯一的.

Postgres的文件说,对于唯一约束的目的,空值是不相等的.

好的.我可以看出这一点.在null处理中这是一个很好的微妙之处:通过将唯一约束列中的所有NULL都视为不相交,我们会延迟唯一约束强制执行,直到有一个实际的非null值作为执行的基础.

那很酷.但这是Postgres失去我的地方.如果唯一约束列中的所有NULL都不相等,正如文档所述,那么我们应该在select select查询中看到所有空值.

select distinct SSN
from People;

"<NULL>"
"123"
Run Code Online (Sandbox Code Playgroud)

不.那里只有一个空值.似乎Postgres有这个错误.但我想知道:还有另一种解释吗?


编辑:

Postgres文档确实指出"在这种比较中,空值被认为是相等的".在SELECT DISTINCT部分.虽然我不明白这个概念,但我很高兴在文档中详细阐述.

小智 23

处理时几乎总是错误的null说:

"nulls在某种程度上就像某某某样,所以它们应该在这里表​​现得像这样 - "

从postgres的角度来看,是一篇关于这一主题的优秀论文.简要总结一下,根据上下文对空值进行不同的处理,并且不会对它们做出任何假设.

  • 感谢您对该文章的链接.作者指出,"考虑NULL的最佳方式是作为几种哲学的弗兰肯斯坦怪物",如果不完全令人满意,那就很有趣. (3认同)

Pet*_*aut 11

最重要的是,PostgreSQL用null做了它,因为SQL标准这样说.

Nulls显然很棘手,可以用多种方式解释(未知值,缺失值等),因此当SQL标准最初编写时,作者必须在某些地方进行一些调用.我说时间已证明它们或多或少是正确的,但这并不意味着不可能有另一种数据库语言轻微地(或疯狂地)处理未知和缺失的值.但是PostgreSQL实现了SQL,所以就是这样.

正如在不同的答案中已经提到的那样,Jeff Davis撰写了一些关于处理空值的好文章和演示文稿.


Jer*_*hka 6

NULL被认为是唯一的,因为NULL它并不代表缺少值。NULL列中的A 是未知值。比较两个未知数时,您不知道它们是否相等,因为您不知道它们是什么。

想象一下,您有两个标有A和B的框。如果您不打开框并且看不到内部,则您永远都不知道其中的内容。如果系统询问您“这两个盒子的内容是否相同?” 您只能回答“我不知道”。

在这种情况下,PostgreSQL将做同样的事情。当要求比较两个NULLS时,它说“我不知道”。这与NULLSQL数据库中疯狂的语义有关。链接到的@JackPDouglas文章是了解NULLs行为的一个很好的起点。请注意:它因供应商而异。

  • 我在一个小观点上略有不同意见:在我看来,null可能表示缺少值或当前未知。我同意其他所有内容,尤其是您不能比较未知数,因为它们是...很好...未知。这就是为什么我在问题中提到的独特行为有意义的原因。我仍然不明白的是,为什么空值虽然在唯一索引中“不同”,却在SELECT语句中不被认为是不同的。 (2认同)