我正在寻找向表添加约束的最佳方法,该表实际上是记录与该表中其余记录之间的关系的唯一索引。 / p>
想象一下下表描述了各种守卫的巡逻(来自之前的守望者场景)
PK PatrolID Integer
FK GuardID Integer
Starts DateTime
Ends DateTime
我们从一个约束开始,指定开始和结束时间必须是逻辑的:
Ends >= Starts
但是我想添加另一个逻辑约束:特定保护(GuardID)不能同时位于两个地方,这意味着对于任何记录,Start / Ends指定的周期不应与为任何其他记录定义的周期重叠由同一个警卫巡逻。
我可以想到两种尝试解决这个问题的方法:
创建一个INSTEAD OF INSERT触发器。然后,此触发器将使用游标通过INSERTED表,检查每条记录。如果任何记录与现有记录冲突,则会引发错误。我对这种方法的两个问题是:我不喜欢在现代版本的SQL Server中使用游标,而且我不确定如何为UPDATE修改相同的逻辑。 INSERTED中的记录复杂性也可能相互冲突。
第二种看似更好的方法是创建一个调用用户定义函数的CONSTRAINT,传递PatrolID,GuardID,Starts和Ends。然后,该函数将执行WHERE EXISTS查询,检查与GuardID / Starts / Ends参数重叠的任何记录,这些记录不是原始的PatrolID记录。但是我不确定这种方法可能产生的副作用。
第二种方法更好吗?有没有人看到任何陷阱,例如一次插入/更新多行(这里我很担心,因为该组中的行可能会发生冲突,这意味着他们“插入”的顺序会有所不同)。有没有更好的方法(例如一些花哨的INDEX技巧?)
答案 0 :(得分:5)
使用after触发器检查是否未违反重叠约束:
create trigger Patrol_NoOverlap_AIU on Patrol for insert, update as
begin
if exists (select *
from inserted i
inner join Patrol p
on i.GuardId = p.GuardId
and i.PatrolId <> p.PatrolId
where (i.Starts between p.starts and p.Ends)
or (i.Ends between p.Starts and p.Ends))
rollback transaction
end
注意:在触发器中回滚事务将终止批处理。与正常的约束违规不同,您将无法捕获错误。
根据您定义时间范围和重叠的方式,您可能需要不同的where子句。例如,如果您希望能够说卫兵#1从6:00到7:00在X,那么在Y 7:00到8:00,上述将不允许。你会想要:
create trigger Patrol_NoOverlap_AIU on Patrol for insert, update as
begin
if exists (select *
from inserted i
inner join Patrol p
on i.GuardId = p.GuardId
and i.PatrolId <> p.PatrolId
where (p.Starts <= i.Starts and i.Starts < p.Ends)
or (p.Starts <= i.Ends and i.Ends < p.Ends))
rollback transaction
end
开始是守卫开始的时间,结束是守卫结束后的无限时刻。
答案 1 :(得分:3)
最简单的方法是使用存储过程进行插入。存储过程可以在单个语句中执行插入:
insert into YourTable
(GuardID, Starts, Ends)
select @GuardID, @Starts, @Ends
where not exists (
select *
from YourTable
where GuardID = @GuardID
and Starts <= @Ends
and Ends >= @Start
)
if @@rowcount <> 1
return -1 -- Failure
根据我的经验,UDF的触发器和约束往往变得非常复杂。它们有副作用,可能需要大量的调试才能弄明白。
存储过程正常工作,并且它们具有额外的优势,即您可以拒绝对客户端的INSERT权限,从而可以对进入数据库的内容进行细粒度控制。
答案 2 :(得分:1)
CREATE TRIGGER [dbo].[emaill] ON [dbo].[email]
FOR INSERT
AS
BEGIN
declare @email CHAR(50);
SELECT @email=i.email from inserted i;
IF @email NOT LIKE '%_@%_.__%'
BEGIN
print 'Triggered Fired';
Print 'Invalid Emaill....';
ROLLBACK TRANSACTION
END
END
答案 3 :(得分:0)