【问题标题】:Supposedly easy trigger据说很容易触发
【发布时间】:2015-03-14 20:20:38
【问题描述】:

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

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')

我从触发器中得到错误。应该如何修改触发器才不会报错?

【问题讨论】:

  • 您使用的是什么关系型数据库?触发器可以在插入之前(在记录实际添加到表之前)或在插入之后(当记录已经添加到表并且通过 SELECT 对您可见时)运行。
  • 为什么不只应用唯一约束?

标签: sql sql-server tsql triggers ddl


【解决方案1】:

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

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

【讨论】:

  • 谢谢你的快速回答,但实际目标是检查 SPO_DateFrom 和 SPO_DateTo 之间的时间段,以便插入的时间段不会与现有时间段重叠,因此它必须在触发器中。知道如何处理触发器吗?
【解决方案2】:

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

因此,触发器将假定已经存在一个值 - 并抛出错误(如设计的那样)。

可以重写触发器以不查看新插入的行,而是查看其他任何内容 - 但正如其他人指出的那样,UNIQUE 约束更简单

【讨论】:

  • 1.约束 - 对重叠时期有帮助吗? 2. 触发器不包括新添加的记录 - 是否有任何特殊语法或者我应该简单地输入 NOT EXISTS (SELECT * FROM inserted WHERE ...)?
【解决方案3】:

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

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')

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

您可能需要将一些&lt;s 更改为&lt;=s 或反之亦然,具体取决于您要使用的间隔定义(即DateFromDateTo 意味着inclusive 或 exclusive 端点用于他们描述的区间?)

【讨论】:

  • 谢谢你,达米安。我将在一分钟内对其进行测试。
  • 完美运行。只有与条件重叠的时期需要一些修正。谢谢。
【解决方案4】:

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

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)
      )

【讨论】:

    猜你喜欢
    • 2010-11-27
    • 2013-01-05
    • 1970-01-01
    • 2017-07-01
    • 2014-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多