如果我没看错,你有两个限制:
- Category Name + LogId 必须唯一;如果 LogId 为 null,则 Name 必须是唯一的。
- 给定的类别名称可能与非空 LogId 或空 LogId 相关联,但不能同时与两者相关联。
UNIQUE
您使用香草约束强制执行 (1) ,如下所示:
alter table dbo.Category add constraint UQ_Category (Name, LogId)
与PRIMARY KEY
约束不同,UNIQUE
约束允许可为空的键,并将空值视为相同“值”的实例。因此,将允许此数据:
insert dbo.Category (LogId, Name) values (null, 'Name1') -- ok
insert dbo.Category (LogId, Name) values (1, 'Name1') -- ok
insert dbo.Category (LogId, Name) values (2, 'Name1') -- ok
insert dbo.Category (LogId, Name) values (3, 'Name1') -- ok
但这将在第一次插入后被拒绝:
insert dbo.Category (LogId, Name) values (null, 'Name1') -- ok
insert dbo.Category (LogId, Name) values (null, 'Name1') -- error
insert dbo.Category (LogId, Name) values (null, 'Name1') -- error
insert dbo.Category (LogId, Name) values (null, 'Name1') -- error
然后对于 (2),您需要一些东西来强制执行排他性,这样如果 Name 与 null LogId 相关联,则它不能与非 null LogId 相关联,反之亦然。为此,您可以在索引视图中按名称和 LogId 的无效性进行分组:
create view dbo.MakeItExclusive
with schemabinding as
select Name
, case when LogId is null then 1 else 0 end as HasNullLogIds
, count_big(*) as _rowcount
from dbo.Category
group by Name
, case when LogId is null then 1 else 0 end
go
create unique clustered index CU_MakeItExclusive on dbo.MakeItExclusive (Name)
go
由于视图有GROUP BY
子句,SQL Server 要求COUNT_BIG(*)
在SELECT
子句中创建索引。
除此之外,它非常简单:按 Name 和 LogId-nullness 分组,然后确保 Name 在结果中是唯一的。如果名称与空 LogId 和非空 LogId 相关联,则会有两行违反约束。