1

我有一个表access,其模式是如下:允许是否可以将唯一约束与Check约束关联?

create table access (
    access_id int primary key identity, 
    access_name varchar(50) not null, 
    access_time datetime2 not null default (getdate()), 
    access_type varchar(20) check (access_type in ('OUTER_PARTY','INNER_PARTY')), 
    access_message varchar(100) not null, 
) 

访问类型只OUTER_PARTY and INNER_PARTY

我想实现的是INNER_PARTY条目应该每天只有一次登录(用户),但OUTER_PARTY可以记录任意次数。所以我想知道是否可以直接做这件事,或者是否有成语来制造这种限制。

我已经检查过这个问题:Combining the UNIQUE and CHECK constraints,但无法将其应用于我的情况,因为它是针对不同的事情。

+0

谢谢你的标签。我愚蠢地把它放在问题上。 在'access_type'上创建聚簇索引是否正确?另外,如果我创建一个视图,不可能更新基础表,是吗?如果我没有错,就不必使用触发器来更新视图,但这会使其变得复杂。 – Animesh 2012-03-22 11:10:14

+0

@Damien_The_Unbeliever - (清理意见)同意。正如答案所示,不需要使用索引视图。 – 2012-03-22 11:37:25

回答

6

一个过滤唯一索引可以被添加到表中。该索引可以基于从access_time列中移除时间分量的计算列。

create table access (
    access_id int primary key identity, 
    access_name varchar(50) not null, 
    access_time datetime2 not null default (SYSDATETIME()), 
    access_type varchar(20) check (access_type in ('OUTER_PARTY','INNER_PARTY')), 
    access_message varchar(100) not null, 
    access_date as CAST(access_time as date) 
) 
go 
create unique index IX_access_singleinnerperday on access (access_date,access_name) where access_type='INNER_PARTY' 
go 

似乎工作:

--these inserts are fine 
insert into access (access_name,access_type,access_message) 
select 'abc','inner_party','hello' union all 
select 'def','outer_party','world' 
go 
--as are these 
insert into access (access_name,access_type,access_message) 
select 'abc','outer_party','hello' union all 
select 'def','outer_party','world' 
go 
--but this one fails 
insert into access (access_name,access_type,access_message) 
select 'abc','inner_party','hello' union all 
select 'def','inner_party','world' 
go 
+0

Damien:是否有你使用sysdatetime()而不是getdate()的原因? sysdatetime不需要更多的存储? – Animesh 2012-03-22 12:03:55

+0

@KishorNanda - 存储取决于数据类型。 'datetime2'在你的情况下,所以额外的精度是“免费” – 2012-03-22 12:06:28

+0

哦,我明白了。你的回答对我很有帮助,同时也提高了我对索引的理解。谢谢。 – Animesh 2012-03-22 12:16:54

2

不幸的是你不能在检查约束上添加一个“if”。我建议使用触发器:

create trigger myTrigger 
on access 
instead of insert 
as 
begin 
    declare @access_name varchar(50) 
    declare @access_type varchar(20) 
    declare @access_time datetime2 

    select @access_name = access_name, @access_type= access_type, @access_time=access_time from inserted 

    if exists (select 1 from access where [email protected]_name and [email protected]_type and [email protected]_time) begin 
    --raise excetion 
    end else begin 
    --insert 
    end 
end 

你将不得不格式化@access_time只考虑日期部分

+0

这可能会失败并允许重复插入多行或快照隔离。最好是让SQL Server强制执行约束,因为它的逻辑是正确的。你也忘记了更新。 – 2012-03-22 11:15:04

+0

我不知道人们为什么如此反对触发器。如有必要,可以更改它以处理多行插入,只需循环插入的表即可。在这种情况下,您将如何使SQL Server强制执行约束?这是问题的全部。 – Diego 2012-03-22 11:19:36

+0

通过视图上的唯一约束。触发器往往效率较低并且不太可能是正确的。 – 2012-03-22 11:21:35