2013-04-11 97 views
0

我需要在SQL Server上創建觸發器,以便只將更新的列寫入具有表名,列名,以前/新值,日期,時間和特定表的用戶的審計表中。我怎樣才能做到這一點?SQL Server自動審計更新列

回答

6

我通常有這樣的表來存儲審計數據 和我得到這個解決方案從這裏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

+0

它很棒!謝謝 ! – user2268955 2013-04-11 08:47:50

+0

好,請記住標記爲答案 – StackTrace 2013-04-11 11:12:41

7

我們使用第三方工具ApexSQL Audit來生成審計觸發器,因爲我們有很多需要審計的表。

如果你不需要第三方工具,你可以在試用模式下使用它,看看他們是如何實現觸發器和存儲表。

下面是我從數據庫中快速獲取的示例觸發器和存儲表。

數據存儲表:

enter image description here

表,用於存儲有關交易細節:

enter image description here

這裏是刪除觸發的樣本。注意內聯

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