使用Check Constraints来确定是否有位列,并根据另一个列值设置为true,可能吗?

时间:2011-01-12 21:53:57

标签: sql sql-server sql-server-2008

要精心制作,

是否可以强制执行一条规则,其中只有一个记录条目可以将名为“IsPrimaryUser”的列设置为true,而将按其他列分组的所有其他列设置为false。决定哪个条目具有真正的'IsPrimaryUser'字段的条件将是CompanyId列。

我只对使用检查约束是否可以完成感兴趣。显然,有一种类似于此的SQL方法。

示例:

用户表

int UserId | int CompanyId |位IsPrimaryUser

数据:

UserId  |  CompanyId  | IsPrimaryUser
   1          1              1
   2          1              0
   3          1              0
   4          1              0
   5          2              1
   6          2              0
   7          2              0
   8          2              0

1 个答案:

答案 0 :(得分:3)

检查约束仅适用于单行,但您可以在约束内使用标量UDF。

您可以使用检查表中其他行的UDF来破坏单行检查。虽然与可以单独访问DELETED虚拟表和进程的触发器不同,但SQL Server似乎在某种事务中保存记录,并在更改后在EACH行执行CHECK,然后最终批量接受或中止CRUD。

查看此测试用例

创建表

create table usertable (UserId int,
CompanyId int, IsPrimaryUser int)

填充

insert usertable select
1, 1, 1 union all select
2, 1, 0 union all select
3, 1, 0 union all select
4, 1, 0 union all select
5, 2, 1 union all select
6, 2, 0 union all select
7, 2, 0 union all select
8, 2, 0

标量函数助手

create function dbo.anyprimaryuser(@userid int, @company int) returns bit as
begin
return
    case when exists (
    select * from usertable
    where companyid=@company and isprimaryuser=1 and userid<>@userid)
    then 1 else 0 end
end

CHECK约束

alter table usertable add constraint usertable_ck1
check (isprimaryuser=0 or dbo.anyprimaryuser(userid,companyid)=0)

测试

insert usertable select 9,2,1 -- fail

insert usertable select 9,2,0 -- ok

insert usertable select 19,4,1 union all select 20,4,0 -- ok

insert usertable select 19,3,1 union all select 20,3,0 union all select 21,3,1
-- not ok, accepting the multi-row insert will breach the constraint

update usertable set IsPrimaryUser=1-IsPrimaryUser where CompanyId=4
-- ok! sets one and unsets the other in one go

(注意)我在Martin的评论

之后更新了答案