我有这张桌子:
CREATE TABLE Category
(
LogId int NULL,
Name varchar(30) NOT NULL
)
具有不同LogId
的两个类别可能具有相同的Name
,但类别可能与具有空LogId
的类别不同。
有没有办法强制执行此约束?
我尝试在此视图上创建唯一索引:
create view Category_LogId_Name
with schemabinding
as
select
LogId,
Name
from
dbo.Category
where
LogId is null
union all
select
b.LogId,
a.Name
from
dbo.Category a
cross join dbo.Log b
where
a.LogId is null
但是试图创建索引:
create unique clustered index un_Category_LogId_Name on Category_LogId_Name (LogId, Name)
产生此错误:
无法在视图'Category_LogId_Name'上创建索引,因为它包含一个或多个UNION,INTERSECT或EXCEPT运算符。考虑为每个查询创建一个单独的索引视图,该视图是原始视图的UNION,INTERSECT或EXCEPT运算符的输入。
有不同的方法吗?
答案 0 :(得分:3)
如果我正确地读你,你有两个限制:
使用vanilla 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与一个空的LogId相关联,它就不能与一个非null的LogId相关联,而且-versa。为此,您可以在索引视图中按名称和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
来创建索引。
除此之外,它非常简单:按名称和LogId-nullness分组,然后确保Name在结果中是唯一的。如果名称与空LogId和非空LogId相关联,则会有两行违反约束。