如何在SQL Server中将用户名列添加到触发器中以进行审计跟踪

时间:2015-06-19 09:32:28

标签: sql-server triggers

我使用以下代码(由Pop Rivett的SQL博客提供)在SQL Server表上为审计跟踪创建触发器。

除了一个例外,它效果很好。我需要记录究竟是谁做出了改变。如上所述,系统用户字段应该这样做,但只有在数据库中直接进行更改时才有效。如果通过网络应用程序完成,则系统用户是网络帐户而不是单个用户。

每个要修改的表都有一个UserName列,它从.NET User.Identity.Name开始,我想将其添加到每一行,但是当我尝试访问inserted.UserName时或deleted.UserName我遇到了错误。

如何添加列以捕获此信息?

CREATE TRIGGER [dbo].[tr_Grading] 
ON [dbo].[Grading] 
FOR INSERT, UPDATE, DELETE
AS
    DECLARE @bit INT ,
            @field INT ,
            @maxfield INT ,
            @char INT ,
            @fieldname VARCHAR(128) ,
            @TableName VARCHAR(128) ,
            @PKCols VARCHAR(1000) ,
            @sql VARCHAR(2000), 
            @UpdateDate VARCHAR(21) ,
            @SystemName VARCHAR(128) ,
            @Type CHAR(1) ,
            @PKSelect VARCHAR(1000)

    -- change @TableName to match the table to be audited
    SELECT @TableName = 'Grading'

    -- date and user
    SELECT @SystemName = SYSTEM_USER,
           @UpdateDate = CONVERT(VARCHAR(8), GETDATE(), 112) 
                          + ' ' + CONVERT(VARCHAR(12), GETDATE(), 114)

   -- Action
   IF EXISTS (SELECT * FROM inserted)
      IF EXISTS (SELECT * FROM deleted)
           SELECT @Type = 'U'
      ELSE
           SELECT @Type = 'I'
   ELSE
       SELECT @Type = 'D'

    -- get list of columns
    SELECT * INTO #ins FROM inserted
    SELECT * INTO #del FROM deleted

    -- Get primary key columns for full outer join
    SELECT @PKCols = COALESCE(@PKCols + ' and', ' on') 
           + ' i.' + c.COLUMN_NAME + ' = d.' + c.COLUMN_NAME
    FROM    
        INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
        INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
    WHERE   
        pk.TABLE_NAME = @TableName
        AND CONSTRAINT_TYPE = 'PRIMARY KEY'
        AND c.TABLE_NAME = pk.TABLE_NAME
        AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

    -- Get primary key select for insert
    SELECT @PKSelect = COALESCE(@PKSelect+'+','') 
                       + '''<' + COLUMN_NAME 
                       + '=''+convert(varchar(100), coalesce(i.' + COLUMN_NAME +',d.' + COLUMN_NAME + '))+''>''' 
    FROM    
        INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
        INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
   WHERE
        pk.TABLE_NAME = @TableName
        AND CONSTRAINT_TYPE = 'PRIMARY KEY'
        AND c.TABLE_NAME = pk.TABLE_NAME
        AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

    IF @PKCols IS NULL
    BEGIN
         RAISERROR('no PK on table %s', 16, -1, @TableName)
         RETURN
    END

    SELECT         
        @field = 0, 
        @maxfield = MAX(ORDINAL_POSITION) 
    FROM 
        INFORMATION_SCHEMA.COLUMNS 
    WHERE 
        TABLE_NAME = @TableName

    WHILE @field < @maxfield
    BEGIN  
        SELECT @field = MIN(ORDINAL_POSITION) 
        FROM INFORMATION_SCHEMA.COLUMNS 
        WHERE TABLE_NAME = @TableName 
          AND ORDINAL_POSITION > @field

        SELECT @bit = (@field - 1 )% 8 + 1
        SELECT @bit = POWER(2,@bit - 1)
        SELECT @char = ((@field - 1) / 8) + 1

        IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @bit > 0 OR @Type IN ('I','D')
        BEGIN
           SELECT @fieldname = COLUMN_NAME 
           FROM INFORMATION_SCHEMA.COLUMNS 
           WHERE TABLE_NAME = @TableName 
             AND ORDINAL_POSITION = @field

           SELECT @sql = '

           INSERT Audit(Type, TableName, PK, FieldName, OldValue, NewValue, 
                        UpdateDate, SystemName)
              SELECT 
                  ''' + @Type + ''',
                  ''' + @TableName + ''',
                  ' + @PKSelect + ',''' + @fieldname + ''''
   + ',convert(varchar(1000),d.' + @fieldname + ')'
   + ',convert(varchar(1000),i.' + @fieldname + ')'
   + ',''' + @UpdateDate + ''''
   + ',''' + @SystemName + ''''
   + ' from #ins i full outer join #del d'
   + @PKCols
   + ' where i.' + @fieldname + ' <> d.' + @fieldname 
   + ' or (i.' + @fieldname + ' is null and  d.' + @fieldname + ' is not null)' 
   + ' or (i.' + @fieldname + ' is not null and  d.' + @fieldname + ' is null)' 
           EXEC (@sql)
   END
 END
GO

1 个答案:

答案 0 :(得分:0)

查看触发器,最后将动态SQL别名作为I插入,因此请尝试插入I.Username,并在选择部分中删除D.Username

以下是对触发器中发生的事情的快速解释。

选择临时表格

SELECT * INTO #ins FROM inserted

SELECT * INTO #del FROM deleted

选择语句中的别名临时表

 from #ins i full outer join #del d

编辑 - 如何将用户名设置为变量

我喜欢在过程/触发器的顶部设置变量,如下所示:

   CREATE TRIGGER [dbo].[tr_Grading] 
    ON [dbo].[Grading] 
    FOR INSERT, UPDATE, DELETE
    AS
        DECLARE @bit INT ,
                @field INT ,
                @maxfield INT ,
                @char INT ,
                @fieldname VARCHAR(128) ,
                @TableName VARCHAR(128) ,
                @PKCols VARCHAR(1000) ,
                @sql VARCHAR(2000), 
                @UpdateDate VARCHAR(21) ,
                @SystemName VARCHAR(128) ,
                @Type CHAR(1) ,
                @PKSelect VARCHAR(1000),
                @UsernameI VARCHAR(1000),
                @UsernameD Varchar(1000)


        -- Set Username
        SET @UsernameI = (SELECT Top 1 Username FROM Inserted)
        SET @UsernameD = (SELECT Top 1 Username FROM Deleted)