针对整个表验证UPDATE和INSERT语句

Dav*_*vid 3 sql-server validation triggers sql-server-2005 constraints

我正在寻找向表添加约束的最佳方法,该表实际上是记录与该表中其余记录之间关系的唯一索引.

想象一下下表描述了各种警卫的巡逻(来自之前的守望者场景)

PK  PatrolID Integer
FK  GuardID  Integer
    Starts   DateTime
    Ends     DateTime
Run Code Online (Sandbox Code Playgroud)

我们从一个约束开始,指定开始和结束时间必须是逻辑的:

Ends >= Starts
Run Code Online (Sandbox Code Playgroud)

不过,我要添加另一个逻辑约束:一个特定的后卫(GuardID)不能在同一时间两个地方,这意味着任何记录的开始/结束指定的时间内不应该由任何其他巡逻定义的时段重叠同样的警卫.

我可以想到两种尝试解决这个问题的方法:

创建一个INSTEAD OF INSERT触发器.然后,此触发器将使用游标通过INSERTED表,检查每条记录.如果任何记录与现有记录冲突,则会引发错误.我对这种方法的两个问题是:我不喜欢在现代版本的SQL Server中使用游标,而且我不确定如何为UPDATE修改相同的逻辑.INSERTED中的记录的复杂性也可能相互冲突.

第二种看似更好的方法是创建一个调用用户定义函数的CONSTRAINT,传递PatrolID,GuardID,Starts和Ends.然后,该函数将执行WHERE EXISTS查询,检查与GuardID/Starts/Ends参数重叠的任何记录,这些记录不是原始的PatrolID记录.但是我不确定这种方法可能产生的副作用.

第二种方法更好吗?有没有人看到任何陷阱,例如一次插入/更新多行(这里我很担心,因为该组中的行可能会发生冲突,这意味着他们"插入"的顺序会有所不同).有没有更好的方法来做到这一点(比如一些花哨的INDEX技巧?)

Sha*_*nce 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
Run Code Online (Sandbox Code Playgroud)

注意:在触发器中回滚事务将终止批处理.与正常的约束违规不同,您将无法捕获错误.

您可能需要一个不同的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
Run Code Online (Sandbox Code Playgroud)

开始是守卫开始的时间,结束是守卫结束后的无限小时刻.