【问题标题】:SQL Server update trigger is not workingSQL Server 更新触发器不起作用
【发布时间】:2013-04-23 22:02:40
【问题描述】:

有一个具有更新触发器的数据库表。每当更新一列时,都会自动计算其中一列。

我已经调整了触发器,我想再次在所有行上运行它。在 SQL Server Management Studio 中,如果我在表上选择“编辑前 200 行”并编辑其中一行,则更新触发器将起作用。但是当我写这样的查询时:

UPDATE MyTable
SET SomeIrrelevantColumn = 0

触发器不起作用,应该由触发器计算的列保持不变。

如何在所有行上手动运行触发器?

编辑:这是触发器:

USE [MY_DATABASE]
GO
/****** Object:  Trigger [dbo].[MY_TABLE_AUER]    Script Date: 04/24/2013 00:05:23 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[MY_TABLE_AUER] 
   ON  [dbo].[MY_TABLE] 
   AFTER UPDATE
AS 
DECLARE @UPD_COLUMN_A int,
        @INS_COLUMN_A int,
        @UPD_COLUMN_B int,
        @UPD_COLUMN_C varchar(255),
        @UPD_COLUMN_D varchar(255),
        @UPD_COLUMN_E int,
        @UPD_COLUMN_F datetime
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    SELECT @UPD_COLUMN_A = _Column_A,
           @UPD_COLUMN_B =_Column_B,
           @UPD_COLUMN_C = COLUMN_C,
           @UPD_COLUMN_D = [Column_D]
      FROM DELETED;
    IF @UPD_COLUMN_D not like '%SomeString%'
    BEGIN
      SELECT @INS_COLUMN_A = _Column_A
        FROM INSERTED;
      IF @UPD_COLUMN_A != @INS_COLUMN_A
      BEGIN
        SELECT @UPD_COLUMN_E = MAX([_Column_B]),
               @UPD_COLUMN_F = MAX([_Column_G])
          FROM MY_TABLE
         WHERE COLUMN_C = @UPD_COLUMN_C
           AND [Column_D] LIKE '%SomeString%';
        UPDATE MY_TABLE 
           SET COLUMN_E = @UPD_COLUMN_E,
               COLUMN_F = @UPD_COLUMN_F
         WHERE [_Column_B] = @UPD_COLUMN_B;
        UPDATE MY_TABLE 
           SET COLUMN_H = @UPD_COLUMN_B
         WHERE [_Column_B] = @UPD_COLUMN_E; 
      END
    END
END

【问题讨论】:

  • 您必须显示触发代码,否则我们只能猜测。
  • 可能是触发代码有错误,可以发一下吗?
  • 刚刚添加了触发代码。
  • 您的触发器做了一个非常常见但错误的假设,即它将每行执行一次。它没有,它每个 action 执行一次 - 所以当你更新整个表格时,我敢打赌,如果你仔细观察,你会看到 one 行已更新。
  • 您可以学习如何编写 SQL 代码来一次更新多行而不是一次。

标签: sql sql-server database triggers sql-update


【解决方案1】:

您的触发器做了一个非常常见但错误的假设,即它将每行执行一次。它没有,它每个动作执行一次——所以当你更新整个表时,我敢打赌,如果你仔细观察,你会看到更新了一行。 * 感谢 Aaron Bertrand 对此介绍段落的评论。

您需要研究如何基于 JOIN(使用 inserteddeleted 元表)执行更新。例如:

CREATE TRIGGER TR_Sample_U ON dbo.Sample FOR UPDATE -- AFTER is default so not needed
AS
IF EXISTS ( --check for disallowed modifications
   SELECT *
   FROM
      Inserted I
      INNER JOIN Deleted D
         ON I.SampleID = D.SampleID
   WHERE
      I.Something <> D.Something
      AND I.UpdateDate = D.UpdateDate
)
ROLLBACK TRAN;

这些资源也可能对您有所帮助:

【讨论】:

  • 具有讽刺意味的是,无一例外,您指向我的所有链接几乎都与我的风格相同。他们只是继续告诉什么是行不通的,并询问是否有人知道使其工作的解决方案。没有“原创研究”。如果每个人都像你一样行事,那么今天这些答案都不存在。不过还是感谢您分享它们。
  • 然后你又推回来了——太棒了!你得到了你需要的答案。那么我们现在很酷吗?
  • 无论如何,我感谢您退缩并以适当的语气写下答案。不过,我不知道您在评论中所说的“推回”是什么意思。
  • 你为自己说话,让我知道你对你得到的东西不满意。
【解决方案2】:

我发现一种方法如下:

UPDATE A
SET A.SOME_COLUMN = D.ANOTHER_COLUMN
FROM MY_TABLE AS A
JOIN inserted AS B ON A.ID = B.ID -- Point 1.
JOIN deleted AS C ON A.ID = C.ID
JOIN (SELECT MAX(ID) AS OTHER_ID, GROUP_ID AS OTHER_GROUP_ID -- Point 2.
      FROM MY_TABLE AS E
      WHERE E.SOME_STRING_COLUMN LIKE '%SomeString%'
      GROUP BY E.GROUP_ID) AS D ON A.GROUP_ID = D.OTHER_GROUP_ID
WHERE C.SOME_BOOL_COLUMN != B.SOME_BOOL_COLUMN -- Point 3.
      AND C.SOME_STRING_COLUMN NOT LIKE '%SomeString%'
  1. 在基本的UPDATE 语句之后,我继续将该表加入到“插入”和“删除”特殊表(有关http://www.mssqltips.com/sqlservertip/2342/understanding-sql-server-inserted-and-deleted-tables-for-dml-triggers/ 的更多信息)。这样,我只会遍历已更新的行,而不会与其他行混淆。您只能加入其中之一,但我需要查看更新操作前后值之间的一列差异。所以这就是我同时使用它们的原因。 'deleted' 具有更新操作之前状态的行,'inserted' 具有更新操作之后状态的行。
  2. 之后,我需要在整个表中找到由当前正在更新的行中的值计算的行。就我而言,当前行有一个父级,我想找到该行。这两行共享相同的GROUP_ID,我进行了字符串测试以确保我得到的新行符合父级条件,您可以在那里定义任何类型的过滤器。基本上,您编写另一个查询以根据更新的行查找该行,然后为返回的表创建另一个 JOIN
  3. 最后,我使用了WHERE 子句来确保仅通过查看SOME_BOOL_COLUMN 列来更新已更改状态的行。您可以在此处放置任何类型的标准。如您所见,我检查了更新前后列状态之间的差异。

不过,这里写的任何东西都持保留态度,因为它来自几乎没有 SQL 经验的人。

【讨论】:

  • 我看到您获得了具有一定价值的Max。您是否使用该最大值执行更新,如果是,您是否打算将所有修改的行更新为相同的值?此外,如果可以并发(多个客户端同时进行修改),那么您需要添加FROM MY_TABLE E WITH (UPDLOCK, HOLDLOCK) 以确保两个客户端不会同时获得相同的Max 值。这种表锁定可能是高吞吐量的杀手。如果模式相似,This 可能会有所帮助。
  • @ErikE,我想要对 MAX 做的只是找到父行(它在 GROUP_ID 列共享相同的值,当前行正在更新),然后只是将当前行的父行设置为具有最大ID 的行,这是唯一键。所以基本上,MAX(ID) 值最终成为当前更新行的父引用。
  • 除此之外,即使并发是一个问题,也绝对不是我的问题。今天早上我花了几个小时才完成这项工作。如果它成为一个问题,有更多知识的人可以看看它(但我怀疑在我工作的地方是否有任何人可以处理它)。但感谢您的意见,以及您在回答中分享的资源,它们肯定有帮助。
【解决方案3】:

请尝试使用下面的代码..in 触发器..!

//Check if the column is getting updated

if (Update(column1) or Update(Column2)...etc)

Begin

  Write the entire logic inside this loop..!

End

希望这有效..!!!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-03
    • 1970-01-01
    • 2013-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多