Sql server SQL Server:使用一个查询触发更新

Sql server SQL Server:使用一个查询触发更新,sql-server,Sql Server,我有一个触发器,当某些列被更新时,它会记录到审计表中。我在删除一列后使用更新的列时遇到了很多问题,所以我正在重写触发器只使用更新,这似乎很有效 但是,我在每个IF语句中不断重复查询,如果UpdateColumnName,请参见下面的代码,因为我需要动态更改列名。有没有办法欺骗我,这样我就不必重复插入查询了 我不确定下面的性能,但您可以使用unpivot获得类似的效果 将任何审计表作为 CREATE TABLE dbo.AuditLog ( DatabaseName SYSN

我有一个触发器,当某些列被更新时,它会记录到审计表中。我在删除一列后使用更新的列时遇到了很多问题,所以我正在重写触发器只使用更新,这似乎很有效

但是,我在每个IF语句中不断重复查询,如果UpdateColumnName,请参见下面的代码,因为我需要动态更改列名。有没有办法欺骗我,这样我就不必重复插入查询了


我不确定下面的性能,但您可以使用unpivot获得类似的效果

将任何审计表作为

CREATE TABLE dbo.AuditLog
(
    DatabaseName        SYSNAME,
    SchemaName          SYSNAME,
    TableName           SYSNAME,
    ColumnName          SYSNAME,
    KeyValue            SQL_VARIANT,
    OldValue            SQL_VARIANT,
    NewValue            SQL_VARIANT,
    TransactionType     VARCHAR(10),
    LogDate             AS CURRENT_TIMESTAMP
)
GO
然后是要记录的“普通”表

CREATE TABLE dbo.MyTable
(
    Id INT IDENTITY(1,1),
    Col1 INT,
    Col2 INT,
    Col3 INT
)
GO
使用unpivot记录更改的触发器是

编辑以供评论:

IF @action = @UPDATED
BEGIN
    INSERT INTO dbo.AuditLog (DatabaseName, SchemaName, TableName, ColumnName, KeyValue, OldValue, NewValue, TransactionType)
    SELECT @DATABASENAME, @SCHEMANAME, @TABLENAME, upiv_inserted.ColumnName, 
            upiv_inserted.Id, upiv_deleted.OldValue, upiv_inserted.NewValue, 'UPDATE'
    FROM (
        SELECT ColumnName, Id, NewValue
        FROM inserted
        CROSS APPLY (
        VALUES
            ('Col1', Col1),
            ('Col2', Col2)
        ) upiv (ColumnName, NewValue)
    ) upiv_inserted
    JOIN (
        SELECT  ColumnName, Id, OldValue
        FROM deleted
        CROSS APPLY (
        VALUES
            ('Col1', Col1),
            ('Col2', Col2)
        ) upiv (ColumnName, OldValue)
    ) upiv_deleted
    ON upiv_deleted.Id = upiv_inserted.Id
    AND upiv_deleted.ColumnName = upiv_inserted.ColumnName
    WHERE upiv_deleted.OldValue <> upiv_inserted.NewValue
END

天哪这是一个巨大的触发器。我强烈建议你重新考虑一下这个问题——触发器可以在任何时候、多次触发,而你无法控制它被执行的时间和次数。因此,触发器应该灵活而精巧——最多只需要几行代码——但它不应该是这样一个怪物!这肯定会扼杀你的表现!谢谢你的建议。有没有方法不重复更新多个列的查询?谢谢您的回复,我会尝试一下。我遇到了一个与您提到的数据类型类似的问题。基本上,我只是将Col1改为FirstName varchar,将Col2改为LastName varchar,结果失败了。有什么建议吗?@milacay:我相信在提到这个问题之后,这个建议就在这里:您可能需要添加一些派生表来转换某些列。也就是说,不要从插入中选择,而是从选择中选择。。。CASTCol1为…,CASTCol2为。。。从“已插入”或类似的内容中取消插入。@T我,你能给我看一下交叉申请@Updated部分吗?谢谢
ALTER TRIGGER dbo.trg_MyTable_AuditLog
ON dbo.MyTable
FOR INSERT, UPDATE, DELETE
NOT FOR REPLICATION
AS
BEGIN
    DECLARE @DATABASENAME   SYSNAME = DB_NAME(),
            @TABLENAME      SYSNAME = N'MyTable',
            @SCHEMANAME     SYSNAME = N'dbo',
            @INSERTED       SMALLINT = 1,
            @DELETED        SMALLINT = 2,
            @UPDATED        SMALLINT = 3

    DECLARE @action SMALLINT = 0

    SELECT @action = CASE WHEN EXISTS(SELECT 1 FROM inserted) THEN @INSERTED ELSE 0 END + 
                        CASE WHEN EXISTS(SELECT 1 FROM deleted) THEN @DELETED ELSE 0 END 

    IF @action = @INSERTED
    BEGIN
        INSERT INTO dbo.AuditLog (DatabaseName, SchemaName, TableName, ColumnName, KeyValue, OldValue, NewValue, TransactionType)
        SELECT @DATABASENAME, @SCHEMANAME, @TABLENAME, ColumnName, Id, NULL, NewValue, 'INSERT'
        FROM inserted
        UNPIVOT (
            NewValue
            FOR ColumnName IN ( Col1, Col2 )
        ) upiv
    END

    IF @action = @DELETED
    BEGIN
        INSERT INTO dbo.AuditLog (DatabaseName, SchemaName, TableName, ColumnName, KeyValue, OldValue, NewValue, TransactionType)
        SELECT @DATABASENAME, @SCHEMANAME, @TABLENAME, ColumnName, Id, OldValue, Null, 'DELETE'
        FROM deleted
        UNPIVOT (
            OldValue
            FOR ColumnName IN ( Col1, Col2 )
        ) upiv
    END

    IF @action = @UPDATED
    BEGIN
        INSERT INTO dbo.AuditLog (DatabaseName, SchemaName, TableName, ColumnName, KeyValue, OldValue, NewValue, TransactionType)
        SELECT @DATABASENAME, @SCHEMANAME, @TABLENAME, upiv_inserted.ColumnName, 
                upiv_inserted.Id, upiv_deleted.OldValue, upiv_inserted.NewValue, 'UPDATE'
        FROM (
            SELECT ColumnName, Id, NewValue
            FROM inserted
            UNPIVOT (
                NewValue
                FOR ColumnName IN ( Col1, Col2 )
            ) upiv
        ) upiv_inserted
        JOIN (
            SELECT  ColumnName, Id, OldValue
            FROM deleted
            UNPIVOT (
                OldValue
                FOR ColumnName IN ( Col1, Col2 )
            ) upiv
        ) upiv_deleted
        ON upiv_deleted.Id = upiv_inserted.Id
        AND upiv_deleted.ColumnName = upiv_inserted.ColumnName
        WHERE upiv_deleted.OldValue <> upiv_inserted.NewValue
    END
END
GO
INSERT INTO dbo.AuditLog (DatabaseName, SchemaName, TableName, ColumnName, KeyValue, OldValue, NewValue, TransactionType)
SELECT @DATABASENAME, @SCHEMANAME, @TABLENAME, ColumnName, Id, OldValue, Null, 'DELETE'
FROM deleted
CROSS APPLY (
    VALUES
        ('Col1', Col1),
        ('Col2', Col2)
) upiv (ColumnName, OldValue)
IF @action = @UPDATED
BEGIN
    INSERT INTO dbo.AuditLog (DatabaseName, SchemaName, TableName, ColumnName, KeyValue, OldValue, NewValue, TransactionType)
    SELECT @DATABASENAME, @SCHEMANAME, @TABLENAME, upiv_inserted.ColumnName, 
            upiv_inserted.Id, upiv_deleted.OldValue, upiv_inserted.NewValue, 'UPDATE'
    FROM (
        SELECT ColumnName, Id, NewValue
        FROM inserted
        CROSS APPLY (
        VALUES
            ('Col1', Col1),
            ('Col2', Col2)
        ) upiv (ColumnName, NewValue)
    ) upiv_inserted
    JOIN (
        SELECT  ColumnName, Id, OldValue
        FROM deleted
        CROSS APPLY (
        VALUES
            ('Col1', Col1),
            ('Col2', Col2)
        ) upiv (ColumnName, OldValue)
    ) upiv_deleted
    ON upiv_deleted.Id = upiv_inserted.Id
    AND upiv_deleted.ColumnName = upiv_inserted.ColumnName
    WHERE upiv_deleted.OldValue <> upiv_inserted.NewValue
END