【问题标题】:SQL Server auto audit updated columnSQL Server 自动审核更新列
【发布时间】:2013-04-03 05:46:41
【问题描述】:

我需要在 SQL Server 上创建一个触发器,以便仅将更新的列写入审计表,其中包含特定表的表名、列名、先前/新值、日期、时间和用户。我该怎么做?

【问题讨论】:

    标签: sql-server triggers audit


    【解决方案1】:

    我通常有一个这样的表来存储审计数据 我从这里得到了这个解决方案 https://www.simple-talk.com/sql/database-administration/pop-rivetts-sql-server-faq-no.5-pop-on-the-audit-trail/

        CREATE TABLE [dbo].[Audit](
        [AuditID] [int] IDENTITY(1,1) NOT NULL,
        [Type] [char](1) NULL,
        [TableName] [varchar](128) NULL,
        [PK] [varchar](1000) NULL,
        [FieldName] [varchar](128) NULL,
        [OldValue] [varchar](1000) NULL,
        [NewValue] [varchar](1000) NULL,
        [UpdateDate] [datetime] NULL,
        [UserName] [varchar](128) NULL,
     CONSTRAINT [PK_Audit] PRIMARY KEY CLUSTERED 
    (
        [AuditID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    

    然后将以下触发器添加到我要审核的每个表中

    CREATE TRIGGER [dbo].[Tr_DB_Audit] ON [dbo].['YourTableName'] 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) ,
           @UserName VARCHAR(128) ,
           @Type CHAR(1) ,
           @PKSelect VARCHAR(1000)
    
    
    --You will need to change @TableName to match the table to be audited
    SELECT @TableName = 'NameOfTableYouWantToAudit'
    
    -- date and user
    SELECT         @UserName = 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, 
                   UserName)
    select ''' + @Type + ''',''' 
           + @TableName + ''',' + @PKSelect
           + ',''' + @fieldname + ''''
           + ',convert(varchar(1000),d.' + @fieldname + ')'
           + ',convert(varchar(1000),i.' + @fieldname + ')'
           + ',''' + @UpdateDate + ''''
           + ',''' + @UserName + ''''
           + ' 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
    

    你也可以看看这篇文章 http://weblogs.asp.net/jgalloway/archive/2008/01/27/adding-simple-trigger-based-auditing-to-your-sql-server-database.aspx

    【讨论】:

      【解决方案2】:

      我们正在使用第三方工具 ApexSQL Audit 来生成审计触发器,因为我们有很多表需要审计。

      如果您不需要第三方工具,您可以在试用模式下使用它,看看他们是如何实现触发器和存储表的。

      以下是我从数据库中快速获取的示例触发器和存储表。

      数据存储表:

      存储交易详情的表:

      这是一个删除触发器的示例。注意内联注释

      CREATE TRIGGER [dbo].[tr_d_AUDIT_Table_Name]
      ON [dbo].[Table_Name]
      FOR DELETE
      NOT FOR REPLICATION
      AS
      
      BEGIN
          DECLARE 
              @IDENTITY_SAVE              varchar(50),
              @AUDIT_LOG_TRANSACTION_ID       Int,
              @PRIM_KEY               nvarchar(4000),
              @ROWS_COUNT             int
      
          SET NOCOUNT ON
      
      
          Select @ROWS_COUNT=count(*) from deleted
          Set @IDENTITY_SAVE = CAST(IsNull(@@IDENTITY,1) AS varchar(50))
      
          INSERT
          INTO dbo.AUDIT_LOG_TRANSACTIONS
          (
              TABLE_NAME,
              TABLE_SCHEMA,
              AUDIT_ACTION_ID,
              HOST_NAME,
              APP_NAME,
              MODIFIED_BY,
              MODIFIED_DATE,
              AFFECTED_ROWS,
              [DATABASE]
          )
          values(
              'Table_Name',
              'dbo',
              3,  
              CASE 
                WHEN LEN(HOST_NAME()) < 1 THEN ' '
                ELSE HOST_NAME()
              END,
              CASE 
                WHEN LEN(APP_NAME()) < 1 THEN ' '
                ELSE APP_NAME()
              END,
              SUSER_SNAME(),
              GETDATE(),
              @ROWS_COUNT,
              'database_name'
          )
      
          Set @AUDIT_LOG_TRANSACTION_ID = SCOPE_IDENTITY()
      
          INSERT
          INTO dbo.AUDIT_LOG_DATA
          (
              AUDIT_LOG_TRANSACTION_ID,
              PRIMARY_KEY_DATA,
              COL_NAME,
              OLD_VALUE_LONG,
              DATA_TYPE
              , KEY1
          )
          SELECT
              @AUDIT_LOG_TRANSACTION_ID,
              convert(nvarchar(1500), IsNull('[Order_ID]='+CONVERT(nvarchar(4000), OLD.[Order_ID], 0), '[Order_ID] Is Null')),
              'Order_ID',
              CONVERT(nvarchar(4000), OLD.[Order_ID], 0),
              'A'
              ,  CONVERT(nvarchar(500), CONVERT(nvarchar(4000), OLD.[Order_ID], 0))
          FROM deleted OLD
          WHERE
              OLD.[Order_ID] Is Not Null
      
          /*
             Insert statement above is replicated for each column being audited
           */
      
      END  
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-26
        • 1970-01-01
        • 2016-04-12
        • 2014-12-09
        • 2013-12-19
        • 2016-09-18
        • 1970-01-01
        • 2021-03-06
        相关资源
        最近更新 更多