【问题标题】:Create Unique Index One/Only/Single NULL创建唯一索引 One/Only/Single NULL
【发布时间】:2013-05-01 06:10:43
【问题描述】:

似乎在 SQL Server 中,唯一索引将 NULL 视为“只是另一个值”,而不是在 SQL 的其余部分中与 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 列中有任意数量的唯一值)?

【问题讨论】:

  • 我不清楚您真正在寻找什么行为。您使用短语“将 NULL 视为 NULL”就好像它应该是显而易见的,只是从短语中,但对我来说,它不是。我也发现你的第二个例子不清楚。
  • SQL-Server 允许在具有唯一约束的列中最多有一个 NULL。另一个 DBMS(在这种情况下符合标准)允许多个 Null。您可以通过创建唯一的部分索引来绕过它,但我认为这不是您的目标。根本不清楚你的目标到底是什么。
  • 在我看来,与NULL 的任何比较都应该产生NULL,因此如果一列只包含一个NULL,则所有与任何其他值(包括NULL)的比较都应该失败。我可以看到“乐观”并假设NULLs 代表独特的未知数可能很有用,但在我的情况下,这将是悲观的并确保数据库的完整性。
  • 根据标准,与NULL 比较产生UNKNOWN,而不是NULL。这是一个小问题,但如果我们在 SQL 中有一个“布尔”数据类型,我们希望能够将该类型的列设置为 TRUEFALSEUNKNOWN或 i>(如果它可以为空)NULL。只有(据我所知)MySQL 将 NULLUNKNOWN 混为一谈。
  • 我使用 MS SQL,但我对标准一无所知 :P 说真的,我认为许多文档过度简化了 NULL 处理,以至于他们给你留下了一个不完整的心智模型。

标签: sql sql-server null unique-constraint


【解决方案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.

【讨论】:

    【解决方案2】:

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

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

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

    归根结底,您的设计会施加难以实施的异常约束。或许你可以重新审视一下设计。

    【讨论】:

    • 是的,触发器不是最理想的。 :) 我认为我的设计实际上并没有那么不寻常,它只是消除了一点点灵活性,这将大大增加我的查询的复杂性,以便在所有情况下都能正确处理。这种方式可以保证“琐碎”的查询正常工作。
    猜你喜欢
    • 2010-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 2019-12-04
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    相关资源
    最近更新 更多