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

时间:2013-10-31 18:33:39

标签: sql-server

我有一个触发器,在更新某些列时会记录到Audit表。我在删除列后使用columns_updated()时遇到了很多问题,所以我只是使用Update()重写触发器,这似乎有效。

但是,我在每个IF语句If Update(ColumnName)中不断重复查询,请参阅下面的代码,因为我需要动态更改列名。有没有办法欺骗这个,所以我不必继续重复INSERT查询?

IF (@TYPE IN ('U') and UPDATE(ShippingAddressFlag))
    BEGIN   
        SELECT @fieldname = 'ShippingAddressFlag'
        SELECT @SQL =       'insert Audit (Type, TableName, PKCol, PK, FieldName, OldValue, NewValue, UpdateDate, DBUserName, UserID)'
        SELECT @SQL = @SQL +    ' select ''' + @TYPE + ''''
        SELECT @SQL = @SQL +    ',''' + @TableName + ''''
        SELECT @SQL = @SQL +    ',''' + @PKCol + ''''
        SELECT @SQL = @SQL +    ',' + @PK
        SELECT @SQL = @SQL +    ',''' + @fieldname + ''''
        SELECT @SQL = @SQL +    ',convert(varchar(1000),d.' + replace(@fieldname, '''', '') + ')'
        SELECT @SQL = @SQL +    ',convert(varchar(1000),i.' + replace(@fieldname, '''', '') + ')'
        SELECT @SQL = @SQL +    ',''' + @UpdateDate + ''''
        SELECT @SQL = @SQL +    ',''' + @UserName + ''''
        SELECT @SQL = @SQL +    ',''' + @UserID + ''''
        SELECT @SQL = @SQL +    ' from #ins i full outer join #del d'
        SELECT @SQL = @SQL +    @PKCols
        SELECT @SQL = @SQL +    ' where i.' + @fieldname + ' <> d.' + @fieldname 
        SELECT @SQL = @SQL +    ' or (i.' + @fieldname + ' is null and  d.' + @fieldname + ' is not null)' 
        SELECT @SQL = @SQL +    ' or (i.' + @fieldname + ' is not null and  d.' + @fieldname + ' is null)'
        Print @SQL
    END

IF (@TYPE IN ('U') and UPDATE(Amount))
    BEGIN
        SELECT @fieldname = 'Amount'
        SELECT @SQL =       'insert Audit (Type, TableName, PKCol, PK, FieldName, OldValue, NewValue, UpdateDate, DBUserName, UserID)'
        SELECT @SQL = @SQL +    ' select ''' + @TYPE + ''''
        SELECT @SQL = @SQL +    ',''' + @TableName + ''''
        SELECT @SQL = @SQL +    ',''' + @PKCol + ''''
        SELECT @SQL = @SQL +    ',' + @PK
        SELECT @SQL = @SQL +    ',''' + @fieldname + ''''
        SELECT @SQL = @SQL +    ',convert(varchar(1000),d.' + replace(@fieldname, '''', '') + ')'
        SELECT @SQL = @SQL +    ',convert(varchar(1000),i.' + replace(@fieldname, '''', '') + ')'
        SELECT @SQL = @SQL +    ',''' + @UpdateDate + ''''
        SELECT @SQL = @SQL +    ',''' + @UserName + ''''
        SELECT @SQL = @SQL +    ',''' + @UserID + ''''
        SELECT @SQL = @SQL +    ' from #ins i full outer join #del d'
        SELECT @SQL = @SQL +    @PKCols
        SELECT @SQL = @SQL +    ' where i.' + @fieldname + ' <> d.' + @fieldname 
        SELECT @SQL = @SQL +    ' or (i.' + @fieldname + ' is null and  d.' + @fieldname + ' is not null)' 
        SELECT @SQL = @SQL +    ' or (i.' + @fieldname + ' is not null and  d.' + @fieldname + ' is null)'
        Print @SQL
    END

1 个答案:

答案 0 :(得分:0)

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

audit表视为

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记录更改的触发器将是

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

要执行此操作,不透视的列必须是相同的数据类型,因此您可能需要添加一些派生表,并根据需要添加列。

同样,unpivot似乎对varchar和排序规则的长度敏感,一个更好的解决方案,然后取消汇总派生表将使用cross apply而不是{{1} }。 unpivot似乎与字符串长度没有相同的“敏感性”,并且可以更简洁地将表达式应用于列。

这意味着将之前的支点改为

cross apply

编辑评论:

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)