Log changes to database table with trigger

前端 未结 12 2316
醉梦人生
醉梦人生 2021-02-08 18:48

I am looking for a good way to log changes that occur on a particular set of tables in my SQL Server 2005 database. I believe the best way to do this is through a trigger that g

相关标签:
12条回答
  • 2021-02-08 19:18

    Triggers are bad, I'd stay away from triggers.

    If you are trying to troubleshoot something, attach Sql Profiler to the database with specific conditions. This will log every query run for your inspection.

    Another option is to change to calling program to log its queries. This is a very common practice.

    0 讨论(0)
  • 2021-02-08 19:21

    Use a Log Trigger

    There is little reason to capture the actual SQL as there can many different statements that change data in the same way.

    0 讨论(0)
  • 2021-02-08 19:24

    MSSQL has virtual tables named 'Inserted' and 'Deleted', which contain newly-inserted and/or newly-deleted and/or newly-updated data records, which you can access from a trigger ... I use these, to know what data has changed (that's instead of being told what statement changed the data).

    0 讨论(0)
  • 2021-02-08 19:24

    There is a pattern for creating such these triggers called Log Trigger. This is vendor independent and very simple. It is described in here.

    The changes are recorded on another history table. There is no way to grab the exact statement, but it is possible to detect if it was an insertion, and update or a deletion because it creates a "chained" set of records. An insertion is a record with no predecessor, a deletion is a record with no successor, intermediate records are updates. Changes can be detected comparing a record against its predecessor.

    It is very easy to get a snapshot of a single entity (or the whole table) at a given point of time.

    As a bonus, the syntax of this pattern for SQL Server happens to be the simplest one, compared to Oracle, DB2 and MySQL.

    0 讨论(0)
  • 2021-02-08 19:25

    If you just want to keep a log of all transactions (insert, update and delete) in some database tables, then you can run the following script:

    IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'Audit')
      CREATE TABLE LogTable
      (
        LogID [int]IDENTITY(1,1) NOT NULL,
        Type char(1), 
        TableName varchar(128), 
        PrimaryKeyField varchar(1000), 
        PrimaryKeyValue varchar(1000), 
        FieldName varchar(128), 
        OldValue varchar(1000), 
        NewValue varchar(1000), 
        UpdateDate datetime DEFAULT (GetDate()), 
        UserName varchar(128)
      )
    GO
    
    DECLARE @sql varchar(8000), @TABLE_NAME sysname
    SET NOCOUNT ON
    
    SELECT @TABLE_NAME= MIN(TABLE_NAME)
    FROM INFORMATION_SCHEMA.Tables 
    WHERE 
    --query for table that you want to audit
    TABLE_TYPE= 'BASE TABLE' 
    AND TABLE_NAME!= 'sysdiagrams'
    AND TABLE_NAME!= 'LogTable'
    AND TABLE_NAME!= 'one table to not record de log';
    
    WHILE @TABLE_NAME IS NOT NULL
      BEGIN
    
      SELECT 'PROCESANDO ' + @TABLE_NAME;
    
      EXEC('IF OBJECT_ID (''' + @TABLE_NAME+ '_ChangeTracking'', ''TR'') IS NOT NULL DROP TRIGGER ' + @TABLE_NAME+ '_ChangeTracking')
    
    
      SELECT @sql = 'create trigger ' + @TABLE_NAME+ '_ChangeTracking on ' + @TABLE_NAME+ ' 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) ,
            @PKFieldSelect varchar(1000),
            @PKValueSelect varchar(1000)
    
            select @TableName = ''' + @TABLE_NAME+ '''
    
            -- 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 fields select for insert(comma deparated)           
            select @PKFieldSelect = coalesce(@PKFieldSelect+''+'','''') + '''''''' + 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 values for insert(comma deparated as varchar)           
            select @PKValueSelect = coalesce(@PKValueSelect+''+'','''') + ''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 @sql = ''insert LogTable(Type, TableName, PrimaryKeyField, PrimaryKeyValue, UserName)''
            select @sql = @sql + '' select '''''' + @Type + ''''''''
            select @sql = @sql + '','''''' + @TableName + ''''''''
            select @sql = @sql + '','' + @PKFieldSelect
            select @sql = @sql + '','' + @PKValueSelect
            select @sql = @sql + '','''''' + @UserName + ''''''''
    
            select @sql = @sql + '' from #ins i full outer join #del d''
            select @sql = @sql + @PKCols        
    
            exec (@sql)
        ';
      SELECT @sql
      EXEC(@sql)
    
    
      SELECT @TABLE_NAME= MIN(TABLE_NAME) FROM INFORMATION_SCHEMA.Tables 
        WHERE TABLE_NAME> @TABLE_NAME
        --query for table that you want to audit
        AND TABLE_TYPE= 'BASE TABLE' 
        AND TABLE_NAME!= 'sysdiagrams'
        AND TABLE_NAME!= 'LogTable'
        AND TABLE_NAME!= 'one table to not record de log';
    END
    
    0 讨论(0)
  • 2021-02-08 19:28

    Try installing some trigger based third party tool such as ApexSQL Audit and then reverse engineering how they do it. Just install it in trial mode and see how it generates triggers for capturing all different sorts of information.

    Several other things to consider are:

    Storage planning – if you have a lot of updates that means you’ll have a ton of audit data. I’d consider storing that data in separate databases. Especially if you plan on auditing more than one database.

    Managing the amount of data – over time you probably won’t be in a need to keep some very old records. Plan on easy deletion of old data

    Schema changes – what if schema is updated. In worst case your triggers will stop working and throw an error if not created correctly. At best case you’ll miss some of the data. This is also something to consider.

    Taking all of this into account it is probably the most time effective to go with some already developed solution vs creating this from scratch yourself.

    0 讨论(0)
提交回复
热议问题