使用SQL触发器阻止插入重叠的日期范围

时间:2010-10-26 11:28:13

标签: sql tsql sql-server-2008 triggers date-range

我有一个简化的表格如下:

create table Test
(
 ValidFrom date not null,
 ValidTo date not null,
 check (ValidTo > ValidFrom)
)

我想编写一个触发器来阻止插入与现有日期范围重叠的值。我写了一个看起来像这样的触发器:

create trigger Trigger_Test
on Test
for insert
as
begin
 if exists(
  select *
  from Test t
   join inserted i
   on ((i.ValidTo >= t.ValidFrom) and (i.ValidFrom <= t.ValidTo))
 )
 begin
  raiserror (N'Overlapping range.', 16, 1);
  rollback transaction;
  return
 end;
end

但它不起作用,因为我新插入的记录是触发器内的表格测试插入的一部分。因此,插入表中的新记录始终在Test表中与其自身连接。触发器将始终恢复转换。

我无法区分新记录和现有记录。因此,如果我排除相同的日期范围,我将能够在表格中插入多个完全相同的范围。

主要问题是

是否可以编写一个可以按预期工作的触发器,而无需在Test表中添加额外的标识列,我可以使用它从exists()语句中排除新插入的记录,如:

create trigger Trigger_Test
on Test
for insert
as
begin
 if exists(
  select *
  from Test t
   join inserted i
   on (
    i.ID <> t.ID and /* exclude myself out */
    i.ValidTo >= t.ValidFrom and i.ValidFrom <=t.ValidTo
   )
 )
 begin
  raiserror (N'Overlapping range.', 16, 1);
  rollback transaction;
  return
 end;
end

重要:如果不可能没有身份是唯一的答案,我欢迎您提供合理解释原因。

2 个答案:

答案 0 :(得分:5)

我知道这已经回答了,但我最近解决了这个问题,并提出了一些有效的方法(并且对每个插入的行执行单例搜索都表现良好)。请参阅本文中的示例: http://michaeljswart.com/2011/06/enforcing-business-rules-vs-avoiding-triggers-which-is-better/

(并没有使用标识栏)

答案 1 :(得分:3)

两个小的改动,一切都应该正常。

首先,在触发器中添加where子句,以从连接中排除重复记录。那么你不会将插入的记录与自己进行比较:

select *
  from testdatetrigger t
   join inserted i
   on ((i.ValidTo >= t.ValidFrom) and (i.ValidFrom <= t.ValidTo))
  Where not (i.ValidTo=t.Validto and i.ValidFrom=t.ValidFrom)

除此之外,这将允许精确的重复范围,因此您必须在两列中添加唯一约束。实际上,您可能希望每列都有唯一约束,因为默认情况下,同一天开始(或结束)的任何两个范围都是重叠的。