创建唯一索引One / Only / Single NULL

时间:2013-05-01 06:10:43

标签: sql sql-server null unique-constraint

似乎在SQL Server中,唯一索引将NULL视为“只是另一个值”,而不是在SQL的其余部分中将NULL与NULL的比较返回NULL。

假设您有一个表(t)在可空列K上有唯一索引:

K     V
0     32
1     12
3     45

一切都很好。

但它也会允许

K     V
0     32
1     12
3     45
NULL  89     <-- Baaad

反之亦然,它还允许以下内容:

K     V
NULL  89
0     32    <-- not good

我可以看到这可能是一个潜在的灾难,因为我使用NULL键值表示不可能进一步分解的值 - 总计和分解会导致重复计算或不一致。

我可以找到数以千计的问题,其中人们想要做相反的事情(允许多个NULL),但没有人想要将NULL视为NULL。


如何让SQL Server在唯一索引中将NULL视为NULL(并且只允许列中的一个NULL 任意数量的唯一值)?

2 个答案:

答案 0 :(得分:1)

如果Andomar对您想要的内容的解释是正确的,那么如果您的表格已经包含所有可能的K值,那么它可能是可行的:

create table dbo.T (
    K int null,
    V int not null,
)
go
create table dbo.PossibleKs (
    K int not null
)
insert into dbo.PossibleKs (K) values (0),(1),(2)
go
create view dbo.TV
with schemabinding
as
    select pk.K
    from
        dbo.T t
            inner join
        dbo.PossibleKs pk
            on
                t.K = pk.K or
                t.K is null
GO
create unique clustered index IX_TV on dbo.TV (K)

您的测试用例:

insert into dbo.T(K,V) values
(0,     32),
(1,     12),
(3,     45)
go
insert into dbo.T(K,V) values
(NULL,89)
--Msg 2601, Level 14, State 1, Line 1
--Cannot insert duplicate key row in object 'dbo.TV' with unique index 'IX_TV'. The duplicate key value is (0).
--The statement has been terminated.
go
delete from dbo.T
go
insert into dbo.T(K,V) values
(NULL,89)
go
insert into dbo.T(K,V) values
(0,     32)
--Msg 2601, Level 14, State 1, Line 1
--Cannot insert duplicate key row in object 'dbo.TV' with unique index 'IX_TV'. The duplicate key value is (0).
--The statement has been terminated.

答案 1 :(得分:0)

因此,您需要一个null或任意数量的唯一数字。我不认为可以使用约束来可靠地执行。

您可以使用触发器。触发器必须回答以下问题:您是否将行更新为null?是否已经有一行null?您是否正在更新已经null的行?触发器将很复杂且难以维护。

您可以使用存储过程操作表。存储过程可以在事务中执行更新/插入/删除操作。在提交之前,他们可以检查表是否包含一个null或任意数量的其他值。你可以合理地保持这一点。

在一天结束时,您的设计会产生难以实施的异常限制。也许你可以重新审视这个设计。