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

Postgresql 为什么Postgres在涉及唯一约束的情况下不一致地处理空值?,postgresql,null,unique,Postgresql,Null,Unique,我最近注意到Postgres在处理具有唯一约束的列中的空值时存在不一致性 考虑一张人员表: 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');

我最近注意到Postgres在处理具有唯一约束的列中的空值时存在不一致性

考虑一张人员表:

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上的唯一约束。到目前为止,一切顺利。但让我们尝试一个空值:

insert into People(pid, name, SSN)
values(1, 'Carol', null);
这很有效

select *    
from People;

0;"Bob";"123"
1;"Carol";"<NULL>"
选择*
来自人民;
0;“鲍勃”;"123"
1.“卡罗尔”;""
唯一列将采用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>"
插入人物(pid、姓名、SSN)
值(2,'Ted',null);
选择*
来自人民;
0;“鲍勃”;"123"
1.“卡罗尔”;""
2.“Ted”;""
是的,我能。现在SSN列中有两行为NULL,即使SSN应该是唯一的

也就是说,为了唯一约束的目的,空值不被认为是相等的

好的。我明白这一点。这是null处理中的一个很好的微妙之处:通过将唯一约束列中的所有null都视为不相交,我们延迟了唯一约束的执行,直到有一个实际的非null值作为执行的基础

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

选择不同的SSN
来自人民;
""
"123"
没有。这里只有一个空值。似乎博士后有这个错误。但我想知道:还有其他解释吗


编辑:


Postgres文档确实指定“在这个比较中,空值被认为是相等的。”。虽然我不理解这个概念,但我很高兴文档中有详细说明。

在处理
null
时,说:

“null在这里的行为类似于某某,*因此它们的行为应该类似于 “这里的某某”


从博士后的角度来看,这是一篇关于这一主题的优秀论文。简单地总结一下,空值根据上下文的不同而被区别对待,不要错误地对它们进行任何假设。

NULL
被认为是唯一的,因为
NULL
并不表示没有值。列中的
NULL
是未知值。当你比较两个未知数时,你不知道它们是否相等,因为你不知道它们是什么

假设你有两个标有A和B的盒子。如果你不打开盒子,看不见里面,你永远不知道里面装的是什么。如果你被问到“这两个盒子里的东西一样吗?”你只能回答“我不知道”


在这种情况下,PostgreSQL将做同样的事情。当被要求比较两个
NULL
s时,它会说“我不知道”。这与SQL数据库中围绕
NULL
的疯狂语义有很大关系。链接到中的文章是理解
NULL
s行为的极好起点。请注意:它因供应商而异。

唯一索引中的多个空值是可以的,因为
x=NULL
对于所有
x
都是假的,特别是当
x
本身为空时。您还将在WHERE子句中遇到这种行为,在WHERE子句中,您必须说
其中x为NULL
其中x不为NULL
,而不是
其中x=NULL
其中x为NULL

底线是,PostgreSQL对NULL做了什么,因为SQL标准这么说

空值显然很棘手,可以用多种方式解释(未知值、缺少值等),因此在最初编写SQL标准时,作者必须在某些地方进行一些调用。我想说,时间或多或少证明了它们的正确性,但这并不意味着不可能有另一种数据库语言处理未知值和缺失值的方式略有不同(或大不相同)。但是PostgreSQL实现了SQL,仅此而已


正如在另一个回答中已经提到的,Jeff Davis写了一些关于处理空值的好文章和演示。

我同意在索引中多个空值(比如它们)是可以的。我反对把它们区分开来。(实际上,由于null未知,所以没有为null定义相等。因此“x=null”不是false,它是未定义的。这就是为什么有一个“is”运算符,所以您可以——正如您所指出的——在WHERE子句中测试null。)@Alan:null不能与任何值或另一个null进行比较。这就是为什么可以有两个唯一的空值。您对区分性的反对来自于将空值视为特殊值的愿望,而事实并非如此。我在一个小问题上略有不同:对我来说,空值可能表示没有值,也可能表示它目前未知。我同意其他一切,尤其是你不能比较未知,因为它们是。。。好。。。不知道。这就是为什么我在问题中提到的独特行为是有意义的。我仍然不明白的是,为什么null虽然在唯一索引中是“不同的”,但在SELECT语句中却不被认为是不同的。@Alan-我想建议“null”值代表DBA希望它代表的任何内容,而明智的DBA使用null只代表一件事,这可能是某种“未知”或“缺席”,但不管是什么,DBA都确切地知道它是什么means@JackPDouglas-有趣。。。我从未想过NULLs意味着DBA想要什么。根据伟大的(数据库)制造商E.F.Codd自己的说法,空值表示“丢失的信息”。[参见C.J.Date的“扩展数据库关系模型以获取更多含义”。Date指出,这会导致我们产生三值逻辑:真、假和未知。Date对此感到非常不安,并建议根本不使用空值并坚持默认值。不管怎样,
select distinct SSN
from People;

"<NULL>"
"123"