如何在NULL列上创建唯一索引?


101

我正在使用SQL Server2005。我想将一列中的值限制为唯一,同时允许NULLS。

我当前的解决方案涉及到如下视图的唯一索引:

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)

还有更好的主意吗?


16
没有机会使用sql 2008?您可以使用“ where”创建过滤索引
Simon_Weaver 2010年

3
您并不是意味着唯一,允许NULL,而是似乎意味着唯一,而是包括多个NULL。否则,将像其他任何值一样对NULL进行索引,并且唯一性约束将按预期方式工作-只是不符合SQL标准,如下面的注释中提到的@pst。
Suncat2000

Answers:


26

可以肯定,您不能这样做,因为它违反了唯一性的目的。

但是,此人似乎有不错的解决方法:http : //sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html


2
看来你提供的实际上是(部分)被复制,而归属从这里链接的内容:decipherinfosys.wordpress.com/2007/11/30/...
汤姆Juergens

77
我不同意它“违反了唯一性的目的”-NULL是SQL中的一个特殊值(在许多方面类似于NaN),需要相应地对待。实际上,SQL Server中不能满足各种SQL规范是一个失败:这是一个请求“正确实现”的链接,该链接的价值是:connect.microsoft.com/SQLServer/feedback/details/299229/…

5
供2008年参考,您可以在dbo.bar(key)上将CREATE UNIQUE INDEX foo设置为ON,其中key不为空;
niico

2
我也不同意“违反唯一性的目的”,NULL不等于NULL,因此您应该能够在可为空的列上创建唯一索引并插入多个null。
Wodzu

105

使用SQL Server 2008,您可以创建过滤索引:http : //msdn.microsoft.com/zh-cn/library/cc280372.aspx。(我看到Simon将此添加为评论,但认为该评论应得到自己的回答,因为该评论很容易被忽略。)

另一个选择是触发检查唯一性的触发器,但这可能会影响性能。


84
create unique index UIX on MyTable (Column1) where Column1 is not null
约恩·舒德罗德(JørnSchou-Rode)2010年

1
注意:目前,SQL Server Management Studio似乎不知道如何创建此类索引,因此,如果您以后修改表,将会感到困惑并尝试删除它,因此请记住重新创建它
Simon_Weaver

3
似乎Microsoft已更新SSMS以支持此功能。我有SSMS 10.50.1617,在“索引属性”对话框中,可以选择“过滤器”页面来编辑过滤器。例如“([[Column1] is NOT NULL)”
Phil Haselden

5
在索引中允许多个null和从索引中过滤null是分开的事情。筛选索引实际上将记录从索引中排除,而其他解决方案将null转换为有用的唯一值。注意区别。
Suncat2000

如果在具有这样的筛选索引的表上使用存储过程,请确保ANSI_NULLSON,否则在尝试插入数据时会出现错误。
阿恩(Arne)2012年

71

计算得出的列技巧被广泛称为“零星破坏者”。我的笔记归功于史蒂夫·卡斯(Steve Kass):

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)
)

这看起来很酷。奇怪地寻找nullbuster不会带来太多的东西。我想知道这是否也对加快搜索速度有用-而不是仅使用1和0的计算列来表示是否为null,如果使用PK可以使索引更有用吗?这个周末要去大桌子上测试一下。
大卫·斯托弗

@DavidStorfer,您不能这样做,因为两个不同表的ID之间可能会发生冲突。
user393274

改进:ISNULL(X,CONVERT(VARCHAR(10),pk))
Faiz 2014年

5
@Faiz:情人眼中有改进。我更喜欢原始的外观。
2014年

@NunoG,这应该是可接受的答案,因为它提供了符合您要求的良好解决方案,而不仅仅是链接可能会消失的外部网站。
弗雷德里克

-3

严格来说,一个唯一的可为空的列(或一组列)只能为NULL(或NULL记录)一次,因为具有相同值(包括NULL)的多个不止一次明显违反了唯一约束。

但是,这并不意味着“唯一的可为空的列”的概念有效。要在任何关系数据库中实际实现它,我们只需要记住,这类数据库旨在进行规范化以正常工作,并且规范化通常涉及添加几个(非实体)额外的表以建立实体之间的关系。

让我们工作一个仅考虑一个“唯一的可为空的列”的基本示例,将其扩展到更多此类列很容易。

假设我们用这样的表表示的信息:

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之间):

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的值对于所有the_relation都是唯一的,并且还请注意,对于the_entity中的每个值,the_relation中最多可以有一个值,因为其上的主键和外键会强制执行此操作。

然后,如果将uniqnull的5值与the_entity id 3关联,则需要:

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对应项,则我们只会这样做:

start transaction;
insert into the_entity (id) values (10); 
commit;

为了对这些信息进行非规范化处理并获取诸如the_entity_incorrect这样的表的数据,我们需要:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

“左外部联接”运算符可确保the_entity中的所有行都将出现在结果中,如果the_relation中没有匹配的列,则将NULL放入uniqnull列中。

请记住,花几天(或几周或几个月)来设计标准化良好的数据库(以及相应的非标准化视图和过程),将为您节省数年(或数十年)的痛苦和资源浪费。


6
正如已经接受的具有五十个投票数的答案的评论中所述,MS Sql Server应该支持它在索引为唯一的列中具有多个null。实施SQL标准不允许这样做是失败的。空不是值,空不等于空,这是多年来的基本SQL规则。因此,您的第一句话是错误的,大多数读者都不会继续阅读。
弗雷德里克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.