SQL Server更新触发器不起作用

时间:2013-04-23 22:02:41

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

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

我已经调整了触发器,我想再次在所有行上运行它。在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

3 个答案:

答案 0 :(得分:6)

你的触发器是一个非常常见但错误的假设,即每行执行一次。它不会,每次操作都会执行一次 - 所以当你更新整个表格时,我敢打赌,如果你仔细观察,你会看到一行被更新了。 *感谢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;

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

答案 1 :(得分:0)

我发现有一种方法可以做到以下几点:

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列来更新已更改状态的行。你可以在这里提出任何一种标准。如您所见,我检查更新前后列的状态之间的差异。
  4. 尽管这里写的任何东西都带有一丝盐味,因为它来自几乎没有SQL经验的人。

答案 2 :(得分:-2)

请尝试使用以下代码..触发器..!

//Check if the column is getting updated

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

Begin

  Write the entire logic inside this loop..!

End

希望这有效.. !!!