如何强制执行此约束?

时间:2012-12-17 21:38:20

标签: sql sql-server sql-server-2008 tsql unique-constraint

我有这张桌子:

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运算符的输入。

有不同的方法吗?

1 个答案:

答案 0 :(得分:3)

如果我正确地读你,你有两个限制:

  1. 类别名称+ LogId必须是唯一的;如果LogId为null,则Name必须是唯一的。
  2. 给定的类别名称可能与非空LogId或空LogId相关联,但不能与两者相关联。
  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相关联,则会有两行违反约束。