【问题标题】:SQL server 2008 trigger not working correct with multiple insertsSQL Server 2008 触发器在多次插入时无法正常工作
【发布时间】:2010-05-03 17:40:05
【问题描述】:

我有以下触发器;

CREATE TRIGGER trFLightAndDestination
ON checkin_flight
AFTER INSERT,UPDATE
AS
BEGIN
    IF NOT EXISTS
    (
                    SELECT 1 
            FROM Flight v
            INNER JOIN Inserted AS i ON i.flightnumber = v.flightnumber
            INNER JOIN checkin_destination AS ib ON ib.airport = v.airport
            INNER JOIN checkin_company AS im ON im.company = v.company
            WHERE i.desk = ib.desk AND i.desk = im.desk
    )
    BEGIN
        RAISERROR('This combination of of flight and check-in desk is not possible',16,1)
        ROLLBACK TRAN       
    END
END

我希望触发器做的是在添加 checkin_flight 的新记录时检查表 Flight、checkin_destination 和 checkin_company。 checkin_flight 的每条记录都包含一个航班号和桌号,乘客需要在这些记录中为该目的地办理登机手续。 表 checkin_destination 和 checkin_company 包含有关仅限某些值机柜台的公司和目的地的信息。向 checkin_flight 添加记录时,我需要航班表中的信息来获取目的地和航班公司以及插入的航班号。需要根据航班、目的地和公司的可用值机组合检查此信息。

我正在使用如上所述的触发器,但是当我尝试插入错误的组合时,触发器允许它。我在这里错过了什么?

编辑 1: 我正在使用以下多重插入语句

INSERT INTO checkin_flight VALUES (5315,3),(5316,3),(5316,2)
//5315 is the flightnumber, 3 is the desknumber to checkin for that flight

编辑 2: 测试了不可能的单行插入,然后错误被正确抛出。所以似乎是多重插入造成了问题。

【问题讨论】:

    标签: sql sql-server sql-server-2008 triggers


    【解决方案1】:

    问题在于您的逻辑允许包含至少一组有效值的任何插入。只有当所有条插入的记录无效时才会失败,而不是任何条插入的记录无效。

    将您的“IF NOT EXISTS(...)”更改为语句“IF EXISTS(...)”并更改您的 SELECT 语句以返回无效航班。

    例如:

    IF EXISTS
    (
                    SELECT 1 
            FROM Flight v
            INNER JOIN Inserted AS i ON i.flightnumber = v.flightnumber
            LEFT JOIN checkin_destination AS ib ON ib.airport = v.airport
                 AND i.desk = ib.desk
            LEFT JOIN checkin_company AS im ON im.company = v.company
                 AND i.desk = im.desk
            WHERE (im.desk IS NULL OR ib.desk IS NULL)
    )
    BEGIN
        RAISERROR('This combination of of flight and check-in desk is not possible',16,1)
        ROLLBACK TRAN       
    END
    

    【讨论】:

    • 谢谢克里斯!成功了,我想我需要在逻辑思维方面进行更多训练
    • 一个小问题,为什么是左连接而不是内连接?因为桌子可以是空的?
    • 我假设否定情况是 checkin_destination 或 checkin_company 中的相应记录不存在;通过 LEFT JOINing 到表然后查找没有记录(因此“im.desk IS NULL”部分)来检查不存在的东西。这只是一个假设,您应该进行调整以纠正它。正如我所说的,目标是让您的查询在 INSERTED 中找到任何无效的记录。
    【解决方案2】:

    我不确定您的业务逻辑,但您需要检查查询是否正确。

    您的问题是IF NOT EXISTS,如果 INSERTED 中 3 行中的 1 行条件为真,则它不存在。您需要对其进行转换以找到问题行并使用IF EXISTS 然后出错。

    但是,当处于触发器中时,最好的出错方法是:

    RAISERROR()
    ROLLBACK TRANSACTION
    RETURN
    

    我有点怀疑缺少 RETURN 是您的问题,但在触发器中出错时最好包含三个 Rs。

    【讨论】:

    • 在我看来,显式回滚通常对触发器来说是危险的,因为它假定了可能没有根据的潜在行为。默认情况下禁用 XACT_ABORT 是有原因的。
    • @Einstein,我不同意。如果我的触发器检测到问题,我总是希望它回滚。也许这只是我和我的编码方式,但我总是希望在错误点回滚,所以我可以将正在发生的事情插入到日志表中。调用代码有责任意识到存在错误,并且它也可以在错误日志中添加其他信息,因为它也终止了。但是,我可以看到您是否有不同的错误处理系统,如果您没有预料到,触发器中的回滚可能会导致您出现问题。
    • @KM 我同意您的处理方式,这也是我现在编码并尝试继续这样做的方式,如果您不希望保存任何损坏的数据,这似乎是最合乎逻辑的方式。
    • @Einstein:也许,如果 SQL Server 2005 之前版本并且不使用 TRY/CATCH。如果您使用 TRY/CATCH 如果不再中止外部代码中的批处理。 SQL 2k5 + TRY/CATCH 使触发器行为更“正常”
    • 应用程序提交事务是完全可以接受的,即使它的成员已经失败。触发器的显式回滚会阻止此操作,并导致应用程序和 RDBMS 之间的事务状态同步丢失。应用程序应该记录错误并管理事务:-) RAISERROR != ROLLBACK .. 回滚对于在错误点提供有用的反馈是不必要的。如果你想要 XACT_ABORT 然后启用它.. 系统将自动为你回滚所有失败。
    【解决方案3】:

    问题是如果只有一个插入的记录是正确的,那么条件就会成立。您必须检查所有记录是否正确,例如:

    if (
      (
        select count(*) from inserted
      ) = (
        select count(*) from flight v
        inner join inserted i ...
      )
    ) ...
    

    【讨论】:

    • 啊,现在我也明白爱因斯坦所说的话了:)听起来确实合乎逻辑!我会试试看。 'if not' 是恢复表达式的正确方法吗?
    • 您的计数确实成功了,但 Chris Shaffers 的解决方案也是如此。感谢您的帮助!
    • @Rob:对,您需要检查计数的差异,因此您将使用<> 运算符而不是=
    【解决方案4】:

    插入的表可以包含多行,因此触发器中的所有逻辑必须能够应用于所有行。想法触发器必须每行触发一次效果是 WRT 触发器的常见误解。 SQL Server 倾向于合并对触发器的调用,以提高在同一事务中发生的性能。

    要修复,您可以从插入的 COUNT() 开始,并将其与匹配条件的 COUNT() 进行比较,如果不匹配则引发错误。

    【讨论】:

    • 不应该选择 1 和连接的组合来解决这个问题吗?
    • @Einstein 说 SQL Server 倾向于合并对触发器的调用,以在同一事务中发生它们时提高性能。 什么?所以如果我这样做BEGIN TRANSACTION; INSERT YourTable VALUES(...);INSERT YourTable VALUES(...); INSERT YourTable VALUES(...); COMMIT; 你是说SQL Server 只会调用一次触发器?没办法!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多