0

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
4

1 回答 1

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对 s 和排序规则的长度很敏感varchar,一个更好的解决方案是使用非透视派生表cross apply而不是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)

编辑评论:

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
于 2013-10-31T20:55:46.753 回答