据说容易触发

时间:2015-01-16 09:05:00

标签: sql sql-server tsql triggers ddl

我已经创建了一个触发器,用于阻止插入的记录,并在表格中存在日期。

CREATE TRIGGER [dbo].[SpecialOffers_Insert]
ON [dbo].[SpecialOffers]
FOR INSERT,UPDATE
AS
SET NOCOUNT ON
IF EXISTS (SELECT * FROM inserted WHERE SPO_DateFrom IN (SELECT SPO_DateFrom FROM dbo.SpecialOffers))
BEGIN
RAISERROR('Error. ', 16, 1)
ROLLBACK TRAN
SET NOCOUNT OFF
RETURN
END
SET NOCOUNT OFF

它被添加到表格中:

CREATE TABLE [dbo].[SpecialOffers](
[SPO_SpoId] [int] IDENTITY(1,1) NOT NULL,
[SPO_DateFrom] [datetime] NOT NULL,
[SPO_DateTo] [datetime] NOT NULL)

该表为空,但在尝试插入此类记录时:

INSERT INTO dbo.SpecialOffers (SPO_DateFrom, SPO_DateTo) VALUES ('2015-01-15','2015-01-15')

我从触发器中得到了错误。我应该如何修改触发器以避免错误?

4 个答案:

答案 0 :(得分:1)

如果目标是阻止表中已存在日期的插入记录,则不需要触发器 - 只需在日期字段上创建唯一约束:

ALTER TABLE [dbo].[SpecialOffers]
ADD CONSTRAINT SpecialOffersUQ UNIQUE (SPO_DateFrom)

答案 1 :(得分:1)

如果您想要一个触发器来防止重叠,为什么不这样说:

CREATE TABLE [dbo].[SpecialOffers](
  [SPO_SpoId] [int] IDENTITY(1,1) NOT NULL,
  [SPO_DateFrom] [datetime] NOT NULL,
  [SPO_DateTo] [datetime] NOT NULL,
  constraint CK_SO_NoTimeTravel CHECK (SPO_DateFrom <= SPO_DateTo)
)
GO
CREATE TRIGGER NoOverlaps
on dbo.SpecialOffers
after insert,update
as
    set nocount on
    if exists (
        select *
        from dbo.SpecialOffers so1
                inner join
            dbo.SpecialOffers so2
                on
                    so1.SPO_DateFrom < so2.SPO_DateTo and
                    so2.SPO_DateFrom < so1.SPO_DateTo and
                    so1.SPO_SpoId != so2.SPO_SpoId
                inner join
            inserted i
                on
                    so1.SPO_SpoId = i.SPO_SpoId
        )
    begin
        RAISERROR('No overlaps',16,1)
        ROLLBACK
    end

示例:

--Works
INSERT INTO SpecialOffers (SPO_DateFrom,SPO_DateTo)
values ('20010101','20011231')
GO
--Fails (Trigger)
INSERT INTO SpecialOffers (SPO_DateFrom,SPO_DateTo)
values ('20010101','20011231')
GO
--Fails (Constraint)
INSERT INTO SpecialOffers (SPO_DateFrom,SPO_DateTo)
values ('20011231','20010101')
GO
--Fails (Trigger)
INSERT INTO SpecialOffers (SPO_DateFrom,SPO_DateTo)
values ('20020101','20021231'),
       ('20020701','20030630')

我还添加了一个检查约束,以便我不必处理触发器中的无意义数据。

您可能必须更改<的某些<=的交换,反之亦然,具体取决于您要使用的时间间隔的定义(即DateFrom和{ {1}}意味着包含独占端点,用于他们描述的时间间隔?)

答案 2 :(得分:0)

由于触发器在触发它的SQL语句的事务上下文中运行,因此在INSERT之后,dbo.SpecialOffers中的一行{您刚刚插入的{1}}值,表中的SPO_DateFrom将成功...

因此,触发器将假定已存在一个值 - 并且它会抛出错误(按设计)。

可以重写触发器以不查看新插入的行,但是其他任何东西 - 但正如其他人所指出的那样,SELECT约束更简单地执行

答案 3 :(得分:0)

您应该检查您找到的行是否实际上不是您刚刚插入的行。改变行

IF EXISTS (
      SELECT * FROM inserted 
      WHERE SPO_DateFrom IN (
          SELECT SPO_DateFrom 
          FROM dbo.SpecialOffers)
      )

IF EXISTS (
      SELECT * FROM inserted a
      WHERE SPO_DateFrom IN (
          SELECT SPO_DateFrom 
          FROM dbo.SpecialOffers b 
          WHERE a.SPO_SpoId <> b.SPO_SpoId)
      )