How to create a unique index on a NULL column?
我正在使用SQL Server 2005.我想将列中的值限制为唯一,同时允许NULLS。
我当前的解决方案涉及视图上的唯一索引,如下所示:
1 2 3 4 5 6 | CREATE VIEW vw_unq WITH SCHEMABINDING AS SELECT Column1 FROM MyTable WHERE Column1 IS NOT NULL CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1) |
有更好的想法吗?
使用SQL Server 2008,您可以创建筛选索引:http://msdn.microsoft.com/en-us/library/cc280372.aspx。 (我看到西蒙补充说这是一个评论,但认为它应该得到自己的答案,因为很容易错过评论。)
另一种选择是检查唯一性的触发器,但这可能会影响性能。
计算出的列技巧被广泛称为"nullbuster";我的笔记记录了Steve Kass:
1 2 3 4 5 6 | CREATE TABLE dupNulls ( pk INT IDENTITY(1,1) PRIMARY KEY, X INT NULL, nullbuster AS (CASE WHEN X IS NULL THEN pk ELSE 0 END), CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster) ) |
很确定你不能这样做,因为它违反了独特的目的。
然而,这个人似乎有一个体面的工作:
http://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html
严格地说,唯一可为空的列(或列集)只能为NULL(或NULL的记录),因为具有相同的值(并且这包括NULL)不止一次明显违反了唯一约束。
但是,这并不意味着"独特的可空列"的概念是有效的;要在任何关系数据库中实际实现它,我们必须记住,这种数据库应该被规范化以正常工作,并且规范化通常涉及添加几个(非实体)额外表来建立实体之间的关系。
让我们只考虑一个"独特的可空列",这是一个基本的例子,很容易将它扩展到更多这样的列。
假设我们这样的表所代表的信息如下:
1 2 3 4 5 6 | CREATE TABLE the_entity_incorrect ( id INTEGER, uniqnull INTEGER NULL, /* we want this to be"unique and nullable" */ PRIMARY KEY (id) ); |
我们可以通过将uniqnull分开并添加第二个表来建立uniqnull值和the_entity之间的关系(而不是让uniqnull"在"the_entity内部)来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | CREATE TABLE the_entity ( id INTEGER, PRIMARY KEY(id) ); CREATE TABLE the_relation ( the_entity_id INTEGER NOT NULL, uniqnull INTEGER NOT NULL, UNIQUE(the_entity_id), UNIQUE(uniqnull), /* primary key can be both or either of the_entity_id or uniqnull */ PRIMARY KEY (the_entity_id, uniqnull), FOREIGN KEY (the_entity_id) REFERENCES the_entity(id) ); |
要将uniqnull的值与the_entity中的行相关联,我们还需要在the_relation中添加一行。
对于the_entity中的行没有关联uniqnull值(即对于我们在the_entity_incorrect中放置NULL的值),我们只是不在the_relation中添加一行。
请注意,uniqnull的值对于所有的in_relation都是唯一的,并且还注意到对于the_entity中的每个值,在the_relation中最多只能有一个值,因为它上面的主键和外键强制执行此操作。
然后,如果uniqnull的值为5与the_entity id为3,我们需要:
1 2 3 4 | START TRANSACTION; INSERT INTO the_entity (id) VALUES (3); INSERT INTO the_relation (the_entity_id, uniqnull) VALUES (3, 5); commit; |
并且,如果the_entity的id值为10没有uniqnull对应物,我们只做:
1 2 3 | START TRANSACTION; INSERT INTO the_entity (id) VALUES (10); commit; |
要对这些信息进行非规范化并获取像the_entity_incorrect这样的表格,我们需要:
1 2 3 4 5 6 7 | SELECT id, uniqnull FROM the_entity LEFT OUTER JOIN the_relation ON the_entity.id = the_relation.the_entity_id ; |
"左外连接"运算符确保来自the_entity的所有行都将出现在结果中,当在replation中不存在匹配列时,将NULL放在uniqnull列中。
请记住,在设计一个规范化的数据库(以及相应的非规范化视图和程序)中花费几天(或数周或数月)的任何努力都将为您节省数年(或数十年)的痛苦和资源浪费。