【问题标题】:TSQL update trigger: joining inserted and deletedTSQL 更新触发器:连接插入和删除
【发布时间】:2011-01-15 06:32:39
【问题描述】:

我在 SQL Server 2008 中有一个更新触发器。如果某些列已被修改,我只需要执行触发器操作。因此,我想检查发生了什么变化。

T-SQL 提供了一个“if update(columnName)”结构。但是,如果已经更新了许多行并且其中只有一个行的特定列值发生了更改,“如果 update()”将不得不返回 true。这将使我对比需要的行多得多的行执行触发操作。

所以我认为我只是加入虚拟删除和插入的表(更新之前和之后的行)并自己比较相关列,而不是使用“if update()”。但是,我怎样才能加入这两个表?我不能使用表的主键,因为它可能已被更新修改。我唯一能想到的就是通过 row_number() 加入,即隐式表排序。但这感觉很不对劲,我不知道 SQL Server 是否真的提供了任何保证,即插入的行与删除的行的顺序相同。

【问题讨论】:

  • 永远不要更改主键。如果您需要更改主键的值,那么您应该使用代理主键,例如身份。
  • 如果“从不更改主键”是铁律,为什么外键约束允许“更新级联/设置 null”?如果主键包含在现实世界中有意义的东西,它可能会在某个时候发生变化。我确实尽量避免它,并且我自己的模式具有不可变的主键,但是我不认为它是绝对的禁忌?在这种特殊情况下,架构设计无论如何都不受我的控制,所以我对此无能为力。
  • 外键可能指的是唯一约束,而不仅仅是PK。这使您的论点 aobut CASCADE 无效
  • 在一个非常简单的系统中,您可以通过更新 PK 来完成。但是,当您进入具有数百个表的非常大的系统时,就会出现问题。 Jane Doe 结婚并成为 Jane Smith。在大多数表格中,我们保留最后更改的用户信息。您将如何在数百个表中级联这种更改,可能有数百万行受到影响?此外,希望您的主键不会是 html 链接(在电子邮件或书签中)的一部分,该链接会在您的更新后中断,或者更糟糕的是打开错误的数据。您会将 PK 更新级联到您的所有活动日志表吗?
  • 我同意。正如已经提到的,我自己的架构设计遵循不变的主键规则。这就引出了另一个问题:你能以某种方式强制执行吗? IE。使主键不可变?我可以想象而不是更新触发器来解决问题。有没有更简单的方法?

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


【解决方案1】:

您的权衡是简单性和可维护性与性能 如果性能不是优先使用 if update(YourTriggerActionColumn) 直接 如果优先考虑性能,那么执行此操作的方法是使用“if update(PrimaryKeyColumn)”,因此如果主键没有更改,则使用插入删除连接,否则如果主键发生更改,则检查“if update(YourTriggerActionColumn)” 由于 PK 不会经常更改,因此大多数时候将使用插入删除连接方法,从而解决您的性能问题。 有点晚了,但只是我的 2 美分 :)

【讨论】:

    【解决方案2】:

    您最好的选择可能是(正如我在上面的评论中提到的)如果可能的话,创建一个新表,其中包括原始数据中的所有数据,但还包括一个不可变的主键(IDENTITY 有效,或者您可以使用一些东西否则,如果您愿意)。然后,您可以公开这个模仿原始表的名称和模式的新表的视图。这将为您提供一个固定 ID,您可以根据需要使用它来跟踪更改。

    所有这一切都假定视图在您的特定情况下可以正常工作——如果应用程序正在做一些非常奇怪的事情,它可能无法正常工作,但如果它只是向它抛出标准 CRUD 样式的 SQL,它应该可以正常工作。

    【讨论】:

      【解决方案3】:

      要防止 PK 发生变化,请将其添加到触发器的顶部:

      IF (UPDATE(yourPKcol1) OR UPDATE(yourPKcol2))
      BEGIN 
          RAISERROR('Primary Key change not permitted!',16,1) 
          ROLLBACK
          RETURN
      END
      

      【讨论】:

        【解决方案4】:

        使用您的设计(允许更改主键)似乎很难构建一致的逻辑。

        说,你有这张桌子:

        id   value
        1    2
        2    1
        

        并发出此操作:

        UPDATE  mytable
        SET     id = CASE WHEN id = 1 THEN 2 ELSE 1 END,
                value = CASE WHEN value = 1 THEN 2 ELSE 1 END
        

        更新两条记录,但保留表格如下:

        id   value
        2    1
        1    2
        

        ,从关系的角度来看,这类似于根本不更改表。

        主键的全部意义在于它们永远不会改变。

        【讨论】:

        • 同意。但是如何禁止修改主键?一般来说?无论如何,这对我来说都不是一个选择,因为桌子的设计不在我的控制之下。请参阅我对 devio 答案的评论。
        • @BuschnicK:使用IF UPDATE(),就像你的问题一样:)
        【解决方案5】:

        如果您使用 IDENTITY 列作为主键,则不会出现更新 PK 列的问题。

        【讨论】:

        • 主键包含多个列。我喜欢避免使用身份密钥(有关这方面的一些论据,请参阅 Joe Celko)。无论如何,表模式不在我的控制之下。这就是触发器的全部意义 - 将对原始表的更改传播到我们自己的。
        • 所以您可以修改您无法控制的表上的触发器,但不能修改架构本身?你能用不可变的主键构造一个新表并公开一个正确命名的视图来代替原始表吗?这将允许您拥有一个可以在触发器中跟踪的固定 ID。
        • 这些表位于我的数据库服务器上,我是管理员,所以从这个意义上说,我可以对它们做任何我想做的事情。但是,它们“属于”商业 ERP 系统,如果我显着更改表格布局或行为,该系统就会崩溃。
        猜你喜欢
        • 2012-08-01
        • 2011-02-27
        • 2011-05-05
        • 2015-04-26
        • 1970-01-01
        • 2012-09-19
        • 2011-11-25
        • 2022-01-15
        • 2010-12-17
        相关资源
        最近更新 更多