web-dev-qa-db-ja.com

更新後のSQL Serverトリガー

このトリガーに問題があります。テーブル全体ではなく、問題の行(更新したばかりの行)に対してのみ、要求された情報を更新したいと思います。

CREATE TRIGGER [dbo].[after_update] 
    ON [dbo].[MYTABLE]
    AFTER UPDATE
    AS 
    BEGIN
          UPDATE MYTABLE 
          SET mytable.CHANGED_ON = GETDATE(),
          CHANGED_BY=USER_NAME(USER_ID())

これが問題の行にのみ適用されることをトリガーに伝えるにはどうすればよいですか?

17
user3927897

これがテスト後の私の例です

CREATE TRIGGER [dbo].UpdateTasadoresName 
ON [dbo].Tasadores  
FOR  UPDATE
AS 
      UPDATE Tasadores 
      SET NombreCompleto = RTRIM( Tasadores.Nombre + ' ' + isnull(Tasadores.ApellidoPaterno,'') + ' ' + isnull(Tasadores.ApellidoMaterno,'')    )  
      FROM Tasadores 
    INNER JOIN INSERTED i ON Tasadores.id = i.id

挿入された特別なテーブルには、更新されたレコードからの情報が含まれます。

17
Juan

これを試してください(更新後ではなく更新)

CREATE TRIGGER [dbo].[xxx_update] ON [dbo].[MYTABLE]
    FOR UPDATE
    AS
    BEGIN

        UPDATE MYTABLE
        SET mytable.CHANGED_ON = GETDATE()
            ,CHANGED_BY = USER_NAME(USER_ID())
        FROM inserted
        WHERE MYTABLE.ID = inserted.ID

    END
6

それを行うのは非常に簡単です。まず、ログを保持するテーブルのコピーを作成します。たとえば、SalesOrderId、FirstName、LastName、LastModifiedの列を持つテーブルdbo.SalesOrderがあります。

Version archieveテーブルは、列SalesOrderVersionArhieveId、SalesOrderId、FirstName、LastName、LastModifiedを持つdbo.SalesOrderVersionArchieveである必要があります。

SalesOrderテーブルにトリガーを設定する方法は次のとおりです。

USE [YOURDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      Karan Dhanu
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
CREATE TRIGGER dbo.[CreateVersionArchiveRow]
   ON  dbo.[SalesOrder]
  AFTER Update
AS 
BEGIN

    SET NOCOUNT ON;
INSERT INTO dbo.SalesOrderVersionArchive

   SELECT *
   FROM deleted;

END

SaleOrderテーブルに変更を加えると、VersionArchieveテーブルに変更が表示されます。

3
kdnerd

この解決策を試してください。

       DECLARE @Id INT
       DECLARE @field VARCHAR(50)

       SELECT @Id= INSERTED.CustomerId       
       FROM INSERTED

       IF UPDATE(Name)
       BEGIN
              SET @field = 'Updated Name'
       END

       IF UPDATE(Country)
       BEGIN
              SET @field = 'Updated Country'
       END

       INSERT INTO CustomerLogs
       VALUES(@Id, @field)

       // OR
       -- If you wish to update existing table records.
       UPDATE YOUR_TABLE SET [FIELD]=[VALUE] WHERE {CONDITION}

古いバージョンのSQL Serverではこれを確認しませんでしたが、これはSQL Server 2012で機能します。

2
Hardik

INSERTEDテーブルにアクセスして、IDまたはテーブルの主キーを取得できる必要があります。この例に似たもの...

CREATE TRIGGER [dbo].[after_update] ON [dbo].[MYTABLE]
AFTER UPDATE AS 
BEGIN
    DECLARE @id AS INT
    SELECT @id = [IdColumnName]
    FROM INSERTED

    UPDATE MYTABLE 
    SET mytable.CHANGED_ON = GETDATE(),
    CHANGED_BY=USER_NAME(USER_ID())
    WHERE [IdColumnName] = @id

トリガーを使用するときに使用可能なINSERTEDおよびDELETEDテーブルに関するMSDNのリンクは次のとおりです。 http://msdn.Microsoft.com/en-au/library/ms191300.aspx

1
Kane

まず最初に、すでに見たようにトリガーはテーブル内のすべてのレコードを更新します。行が変更された場合、フィルタリングを実行しません。

第二に、バッチ内で変更される行は1つだけであり、複数の行が変更される可能性があるため正しくないことを前提としています。

これを適切に行う方法は、仮想の挿入および削除されたテーブルを使用することです: http://msdn.Microsoft.com/en-us/library/ms191300.aspx

0
Sean Gallardy

このスクリプトを試して、一時テーブルTESTTESTを作成し、トリガーが次の順序で呼び出されるときの優先順位を監視します。1)INSTEAD OF、2)FOR、3)AFTER

すべてのロジックはINSTEAD OFトリガーに配置され、いくつかのシナリオをコーディングする方法の2つの例があります...

幸運を...

CREATE TABLE TESTTEST
(
    ID  INT,
    Modified0 DATETIME,
    Modified1 DATETIME
)
GO
CREATE TRIGGER [dbo].[tr_TESTTEST_0] ON [dbo].TESTTEST
INSTEAD OF INSERT,UPDATE,DELETE
AS
BEGIN
    SELECT 'INSTEAD OF'
    SELECT 'TT0.0'
    SELECT * FROM TESTTEST

    SELECT *, 'I' Mode
    INTO #work
    FROM INSERTED

    UPDATE #work SET Mode='U' WHERE ID IN (SELECT ID FROM DELETED)
    INSERT INTO #work (ID, Modified0, Modified1, Mode)
    SELECT ID, Modified0, Modified1, 'D'
    FROM DELETED WHERE ID NOT IN (SELECT ID FROM INSERTED)

    --Check Security or any other logic to add and remove from #work before processing
    DELETE FROM #work WHERE ID=9 -- because you don't want anyone to edit this id?!?!
    DELETE FROM #work WHERE Mode='D' -- because you don't want anyone to delete any records

    SELECT 'EV'
    SELECT * FROM #work

    IF(EXISTS(SELECT TOP 1 * FROM #work WHERE Mode='I'))
    BEGIN
        SELECT 'I0.0'
        INSERT INTO dbo.TESTTEST (ID, Modified0, Modified1)
        SELECT ID, Modified0, Modified1
        FROM #work
        WHERE Mode='I'
        SELECT 'Cool stuff would happen here if you had FOR INSERT or AFTER INSERT triggers.'
        SELECT 'I0.1'
    END

    IF(EXISTS(SELECT TOP 1 * FROM #work WHERE Mode='D'))
    BEGIN
        SELECT 'D0.0'
        DELETE FROM TESTTEST WHERE ID IN (SELECT ID FROM #work WHERE Mode='D')
        SELECT 'Cool stuff would happen here if you had FOR DELETE or AFTER DELETE triggers.'
        SELECT 'D0.1'
    END

    IF(EXISTS(SELECT TOP 1 * FROM #work WHERE Mode='U'))
    BEGIN
        SELECT 'U0.0'
        UPDATE t SET t.Modified0=e.Modified0, t.Modified1=e.Modified1
        FROM dbo.TESTTEST t
        INNER JOIN #work e ON e.ID = t.ID
        WHERE e.Mode='U'
        SELECT 'U0.1'
    END
    DROP TABLE #work

    SELECT 'TT0.1'
    SELECT * FROM TESTTEST
END
GO
CREATE TRIGGER [dbo].[tr_TESTTEST_1] ON [dbo].TESTTEST
FOR UPDATE
AS
BEGIN
    SELECT 'FOR UPDATE'

    SELECT 'TT1.0'
    SELECT * FROM TESTTEST

    SELECT 'I1'
    SELECT * FROM INSERTED

    SELECT 'D1'
    SELECT * FROM DELETED

    SELECT 'TT1.1'
    SELECT * FROM TESTTEST
END
GO
CREATE TRIGGER [dbo].[tr_TESTTEST_2] ON [dbo].TESTTEST
AFTER UPDATE
AS
BEGIN
    SELECT 'AFTER UPDATE'

    SELECT 'TT2.0'
    SELECT * FROM TESTTEST

    SELECT 'I2'
    SELECT * FROM INSERTED

    SELECT 'D2'
    SELECT * FROM DELETED

    SELECT 'TT2.1'
    SELECT * FROM TESTTEST
END
GO

SELECT 'Start'
INSERT INTO TESTTEST (ID, Modified0) VALUES (9, GETDATE())-- not going to insert
SELECT 'RESTART'
INSERT INTO TESTTEST (ID, Modified0) VALUES (10, GETDATE())--going to insert
SELECT 'RESTART'
UPDATE TESTTEST SET Modified1=GETDATE() WHERE ID=10-- gointo to update
SELECT 'RESTART'
DELETE FROM TESTTEST WHERE ID=10-- not going to DELETE
SELECT 'FINISHED'

SELECT * FROM TESTTEST
DROP TABLE TESTTEST
0
Tyler Feldkamp