104

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

我当前的解决方案涉及一个视图上的唯一索引,如下所示:

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)

有更好的想法吗?

4

5 回答 5

107

使用 SQL Server 2008,您可以创建过滤索引:http: //msdn.microsoft.com/en-us/library/cc280372.aspx。(我看到西蒙将此作为评论添加,但认为它应该得到自己的答案,因为评论很容易被遗漏。)

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

于 2010-07-07T02:50:14.447 回答
75

计算列技巧被广泛称为“nullbuster”;我的笔记归功于史蒂夫·卡斯:

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)
)
于 2008-10-10T14:56:44.757 回答
24

很确定你不能这样做,因为它违反了唯一性的目的。

然而,这个人似乎有一个体面的工作: http ://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html

于 2008-10-10T14:17:17.193 回答
0

可以使用过滤谓词来指定要包含在索引中的行。

文档中:

WHERE <filter_predicate> 通过指定要包含在索引中的行来创建过滤索引。过滤索引必须是表上的非聚集索引。为过滤索引中的数据行创建过滤统计信息。

例子:

CREATE TABLE Table1 (
  NullableCol int NULL
)

CREATE UNIQUE INDEX IX_Table1 ON Table1 (NullableCol) WHERE NullableCol IS NOT NULL;
于 2021-02-22T17:43:20.710 回答
-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 与 3 的 the_entity id 相关联,我们需要:

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 列中。

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

于 2015-02-24T05:33:08.527 回答