#1 楼
为什么这样工作?因为可以追溯到那时,有人在不知道或不关心标准说什么的情况下做出了设计决策(毕竟,对于NULL
,我们确实有各种各样的怪异行为,并且可以随意强迫不同的行为)。该决定要求在这种情况下为NULL = NULL
。这不是一个非常明智的决定。他们应该做的是使默认行为符合ANSI标准,并且如果他们确实想要这种特殊行为,则可以通过
WITH CONSIDER_NULLS_EQUAL
或WITH ALLOW_ONLY_ONE_NULL
之类的DDL选项允许它。当然,事后看来是20 / 20。
无论如何,即使不是最干净或最直观的方法,我们现在也都有解决方法。
您可以在SQL中获得正确的ANSI行为。通过创建唯一的,经过过滤的索引,在Server 2008或更高版本中。
作为额外的好处,如果允许多个
NULL
,则最终索引的大小将小于整个表的索引(特别是当它不是索引中的唯一列时,它具有NULL
列,等等)。但是,您可能想知道筛选索引的其他一些限制:筛选索引如何成为更强大的功能
#2 楼
正确。在sql server中,唯一约束或索引的实现只允许一个NULL。还要更正这一点,从技术上讲,它与NULL的定义不符,但这是他们为使它更有用而做的一件事,即使它不是“技术上”正确的。请注意,PRIMARY KEY(也是唯一索引)不允许使用NULL(当然)。评论
这种(SQL Server的)技术性也不符合SQL标准。关于此问题有7年的Connect项目。
–超立方体ᵀᴹ
14-10-17在18:39
@ypercube是的。这就是为什么我说这只是实现而与NULL的定义不符。我没有考虑过滤后的唯一索引(尽管我已将其用于其他用途。)
–肯尼斯·费舍尔
14-10-17在19:27
我不同意这种偏离标准的做法会更加有用。 SQL Server似乎将null当作真正的头等值;但是,null不是值,而更像是非值。
–穿刺者
20 May 30'20:46
是的,这不会使SQL Server更有用。实际上,它使可空列上的唯一索引几乎无用-您几乎可以肯定要允许多个NULL值,并且仅对定义的非NULL值强制执行唯一性。 Microsoft应该已经解决了此问题。
–杰兹
20 Nov 13 '18:41
#3 楼
首先-停止使用短语“空值”,它只会使您误入歧途。而是使用短语“空标记”(null marker)-列中的标记,指示该列中的实际值缺失或不适用(但请注意,该标记并未说明实际上是哪种选择¹)。 >现在,想象一下以下情况(数据库不完全了解建模情况)。
Situation Database
ID Code ID Code
-- ----- -- -----
1 A 1 A
2 B 2 (null)
3 C 3 C
4 B 4 (null)
我们正在建模的完整性规则是“该代码必须唯一”。实际情况违反了这一点,因此数据库不应同时将表2和表4都放在表中。
最安全,最不灵活的方法是在“代码”字段中禁止使用空标记,因此不可能出现数据不一致的情况。最灵活的方法是允许多个空标记,并担心在输入值时的唯一性。
Sybase程序员采用了一种比较安全,不太灵活的方法,即仅允许一个空标记在桌子上-从那以后评论员一直在抱怨。我想微软会继续这种行为,以保持向后兼容性。
¹我确信我读过某个地方,科德考虑过实现两个空标记-一个用于未知,一个用于不适用-但是拒绝了,但找不到参考。我记得正确吗?
P.S.我最喜欢的null引用:Louis Davidson,“ Professional SQL Server 2000数据库设计”,Wrox出版社,2001,第52页。“归结为一个句子:NULL是邪恶的。”
评论
允许使用单个null也不会达到此目标。因为缺少的值可能与其他行之一中的值相同。
–马丁·史密斯
2014年10月20日下午5:13
@MartinSmith说了什么。如果您有检查约束CHECK(值IN('A','B','C','D')),该怎么办?然后,SQL-Server的实现和SQL标准都允许表具有5行(每个值一行,外加1和NULL。)然后,可以说,尽管数据库与其约束一致,但与设计者的意图不一致。该表格最多包含4行。没有可以将NULL更改为不会违反约束的值,除非删除一个或多个行。
–超立方体ᵀᴹ
2014-10-20 8:49
标准将允许6行甚至是106行而不是5行的事实并没有改变它们在这种情况下都以某种方式失败的事实。
–超立方体ᵀᴹ
2014年10月20日上午8:50
@Martin Smith,它可能会,但是再一次,它可能不会-数据库服务器无法分辨,因此它不会冒风险并采取安全的路线。那是Sybase(我认为)程序员决定的,从那以后引起了麻烦(至少可以追溯到Inside SQL Server 6.5,这是我书架上最古老的书,Ron Soukup在其中做出的评论与Aaron Bertrand在回答中所做的大致相同) 。我猜可能会更糟-他们本来可以不强制使用null标记。 :-)
–格林斯通·沃克(Greenstone Walker)
2014-10-20 9:49
@GreenstoneWalker-它不采取“安全”的路线。它假定缺失值不会冲突。创建表#T(A INT NULL UNIQUE);插入到#T VALUES(1),(NULL); UPDATE #T SET A = 1 WHERE A IS NULL;会引发错误。根据您的设计动机理论,应该在第一种情况下防止NULL的插入-因为知识不完整意味着无法保证其值是不同的。
–马丁·史密斯
14-10-20在12:05
#4 楼
从技术上讲这可能不准确,但从哲学上讲,它可以帮助我在晚上入睡...就像其他一些人所说或暗示的那样,如果您认为NULL是未知的,则无法确定是否有一个NULL该值实际上等于另一个NULL值。以这种方式考虑,表达式NULL == NULL的计算结果应为NULL,表示未知。
唯一约束将需要一个确定的值来比较列值。换句话说,当使用相等运算符将单个列值与任何其他列值进行比较时,它必须计算为false才有效。尽管未知通常被认为是虚假的,但它并不是真正的错误。两个NULL值可以相等还是不相等...根本无法确定。
将唯一约束视为可以确定彼此不同的限制值,这很有帮助。 。我的意思是,如果您运行的SELECT看起来像这样:
SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"
鉴于存在唯一约束,大多数人会期望得到一个结果。如果您在ColumnWithUniqueConstraint中允许多个NULL值,那么将不可能使用NULL作为比较值从表中选择单个不同的行。
鉴于此,我相信无论是否关于NULL的定义准确实现的方法,在大多数情况下,绝对要比允许多个NULL值实用得多。
评论
如果存在唯一性约束(在任何实现中,不仅是SQL-Server),您的Select都将给出1个结果。你想说啥?
–超立方体ᵀᴹ
14-10-18在10:38
#5 楼
UNIQUE
约束的主要目的之一是防止重复记录。如果需要一张表,其中有多个记录的值是“未知”,但是不允许两个记录具有相同的“已知”值,那么在对未知值进行分配之前,应该为其分配人工唯一标识符在极少数情况下,具有
UNIQUE
约束并且包含单个null值的列;例如,如果一个表包含列值和本地化文本描述之间的映射,则NULL
的一行将使得可以定义当其他表中的该列为NULL
时应显示的描述。 NULL
的行为考虑到了这种使用情况。否则,我看不到任何在任何列上都具有
UNIQUE
约束的数据库的基础,以允许存在许多相同的记录,但是我看不出有办法防止同时允许多个键值不可区分的记录。声明NULL
不等于自身不会使NULL
的值彼此区分。评论
人工唯一标识符是一个笑话,对不起。对于VIN,您将如何做?如果您不知道这是什么,为什么要化妆呢?只是为了占用额外的磁盘空间?似乎无济于事,可以解决其他一些问题(例如,不想以优雅地处理NULL的方式编写应用程序)。如果您绝对需要知道为什么某物为NULL(例如,存在但未知vs.知道它不存在vs.不知道或不在乎它是否存在),则添加某种状态列。令牌只会导致笨拙的trick流代码来处理它们。
–亚伦·伯特兰(Aaron Bertrand)
14-10-18在21:47
很大程度上取决于唯一性约束的目的。如果将字段用作标识符,则不应为null。在某些情况下(例如VIN),业务规则会建议当某项出现两次时,其中一项必定是错误的,但是某些项可能是“不知道”的,唯一性约束就不像是正确的方法。如果一个人的车辆具有已知的VIN,并且与数据库中的另一个VIN冲突,则可能知道至少一个VIN是错误的,但是最好让数据库报告两个记录的可信值,而不是猜测那是对的。
–超级猫
2014-10-18 22:02
@AaronBertrand:在某些情况下,可能无法为字段填充之前必须先建立一个可能为空的unique-if-not-null字段(例如“配偶ID”),但是在某些情况下一个“独特的”约束将是不够的;如果X.Spouse不为空,则X.Spouse.Spouse = X很有必要。顺便说一句,诸如“配偶”之类的事情也可以这样处理:未婚者的记录不应该以配偶为“ NULL”,而是其自己的ID,在这种情况下,X.spouse.spouse = X规则可以适用于每个人。
–超级猫
2014年10月18日在22:12
评论
评论不作进一步讨论;此对话已移至聊天。