【问题标题】:How to get all the transaction logs (insert update delete) for a specific table in SQL Server 2008如何获取 SQL Server 2008 中特定表的所有事务日志(插入更新删除)
【发布时间】:2011-12-08 17:20:02
【问题描述】:

我想获取应用于 SQL Server 2008 中特定表的所有事务。

我找到了上次使用此脚本更新表的时间:

SELECT OBJECT_NAME(OBJECT_ID) AS DatabaseName, last_user_update,*
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID( 'DBName')
AND OBJECT_ID=OBJECT_ID('tableName')

我想知道该表的所有事务(插入、更新、删除)、它们的日期时间以及应用的查询。

最好的方法是什么?

【问题讨论】:

  • 您的查询不一定显示上次更新表的时间。您将需要第三方日志阅读器工具(和事务日志)来确定历史数据的这一点。这是一个持续的需求吗?
  • 嘿马丁我想我们应该能够在不使用第三方的情况下在 sql 中找到此类信息,因为 sql 为每个数据库提供日志文件
  • 但它没有记录格式。您可以使用sys.fn_dblog,但第三方工具供应商已经花费了很多工时来做这件事。如果这是一个持续的需求,您还没有回答吗?
  • 我有一个任务是只使用 sql 查询而不使用第三方来了解特定表更新(无论是插入还是更新语句)的所有日期
  • 您想这样做是为了调查已经发生的事情,或者您的应用程序需要此功能继续运行?

标签: sql-server-2008 audit-logging


【解决方案1】:

在合理的时间内做到这一点的唯一方法是使用第三方工具(正如 Martin 在第一条评论中所说),例如 ApexSQL Log,它可以读取交易日志并获取您需要的信息。

请注意,为了使其正常工作,您的数据库必须处于完全恢复模式,因为此时 SQL Server 会记录完整的事务详细信息,以便稍后重建。

另一种选择是研究如何使用未记录的 fn_dblog 函数,但这会花费您更多时间,并且您将无法读取分离的日志或事务日志备份。

【讨论】:

    【解决方案2】:

    创建一个触发器,该触发器将创建一个新表 Emp_audit 并在对表employee 进行任何更改时向其添加新元组

    create trigger my_trigger on Employees
    
    AFTER INSERT, UPDATE, DELETE
    AS
    DECLARE @What varchar(30);
    DECLARE @Who varchar(30);
    DECLARE @for int;
    DECLARE @At time;
    DECLARE @COUNTI int;
    DECLARE @COUNTD int;
    select @COUNTI = COUNT(*) from inserted;
    select @COUNTD = COUNT(*) from deleted;
    set @Who = SYSTEM_USER;
    set @At = CURRENT_TIMESTAMP;
    
    if( @COUNTD = 0 and @COUNTI = 1)
        begin
        set @What = 'insert';
        select @for = EmployeeID from inserted i;
        end
    else 
        begin
        if( @COUNTD = 1 and @COUNTI = 0)
            begin 
            set @What = 'delete';
            select @for = EmployeeID from deleted i;
            end
        else  
            begin
            set @What = 'update';
            select @for = EmployeeID from inserted i;
            end
        end
    
         INSERT INTO EMP_Audit Values (@What, @Who, @for, @At);
    

    【讨论】:

      【解决方案3】:

      您最好针对此需求设置审核,而不是尝试从事务日志中追溯提取此信息。

      如果您使用的是企业版,则可以使用内置的 SQL Server Audit 功能,否则通过触发器记录所需信息应该相对简单。

      【讨论】:

        【解决方案4】:

        您可以创建自己的事务日志

        第 1 步:为事务日志创建自己的表

        CREATE TABLE [dbo].[TransactionLogs](
         [TransactionLogID] [bigint] IDENTITY(1,1) NOT NULL,
         [Query] [nvarchar](max) NOT NULL,
         [DateCreated] [datetime] NOT NULL,
        CONSTRAINT [PK_TransactionLogs] PRIMARY KEY CLUSTERED 
        (
         [TransactionLogID] ASC
        )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, 
        ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
        ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
        GO
        

        第 2 步:创建创建日志的存储过程。 (注意:将 YourTablePKColumn 替换为您的表主键列。)

        create procedure  [dbo].[sp_CreateQueryLogs]
        (
            @Query nvarchar(max) = null output,
            @TableName nvarchar(100),
            @YourTablePKColumn nvarchar(30),
            @QueryTypeID tinyint --0 insert, 1 update, 2 delete
        ) 
        as
        begin
            declare @object_id bigint, @column_name nvarchar(100), @collation_name nvarchar(50), @column_name_id nvarchar(100) = null, @column_names nvarchar(max) = '', @column_values nvarchar(max) = '', @column_names_create nvarchar(max) = '', @values nvarchar(max) = '', @user_type_id int, @max_length nvarchar(10), @type_name nvarchar(50), @CreateTempTable nvarchar(max) = '', @is_nullable bit, @value nvarchar(max) = ''
        
            
            create table #tmpValues(ColumnValues nvarchar(max))
        
            insert into #tmpValues(ColumnValues)
            exec('select CAST ( ( select * from ' + @TableName + ' where YourTablePKColumn = ' + @YourTablePKColumn + '
                            FOR XML PATH(''tr''), TYPE 
                ) AS NVARCHAR(MAX) )')
        
            select @values = ColumnValues from #tmpValues
        
            if @QueryTypeID = 0 --insert
                set @Query = 'insert into ' + @TableName + '('
            else if @QueryTypeID = 1 --update
                set @Query = 'update ' + @TableName + ' set '
            else if @QueryTypeID = 2 --dalete
                set @Query = 'delete ' + @TableName + ' '
        
            select @object_id = object_id from sys.tables where name = @TableName
        
            if not cursor_status('local','columnCursor') <= -1
            begin
                close columnCursor;
                deallocate columnCursor;
            end
            declare columnCursor cursor local for
            select name, user_type_id, convert(nvarchar(10), max_length), is_nullable from sys.columns where object_id = @object_id order by column_id ;
            open columnCursor;
            fetch next from columnCursor 
            into @column_name, @user_type_id, @max_length, @is_nullable;
            while @@FETCH_STATUS = 0
            begin
                select @type_name = name, @collation_name = collation_name from sys.types where user_type_id = @user_type_id
                if @column_name_id is null
                    set @column_name_id = @column_name
                else
                begin
                    set @column_names += @column_name + ', '
                    declare @value_keys_start nvarchar(max) = '<' + @column_name + '>', @value_keys_end nvarchar(max) = '</' + @column_name + '>'
        
                    if charindex(@value_keys_start,@values,1) = 0
                    begin
                        if @QueryTypeID = 0 --insert
                            set @column_values += 'null,'
                        else if @QueryTypeID = 1 --update
                            set @column_values += @column_name + ' = null,'
                    end
                    else
                    begin
                        if @QueryTypeID = 0 --insert
                            if @collation_name is null and not (@type_name like '%date%' or @type_name like '%time%')
                                set @column_values += substring(@values, charindex(@value_keys_start,@values,1) + len(@value_keys_start), charindex(@value_keys_end,@values,1) - (charindex(@value_keys_start,@values,1) + len(@value_keys_start))) + ','
                            else if @type_name like '%date%' or @type_name like '%time%'
                                set @column_values += '''' + replace(substring(@values, charindex(@value_keys_start,@values,1) + len(@value_keys_start), charindex(@value_keys_end,@values,1) - (charindex(@value_keys_start,@values,1) + len(@value_keys_start))),'T',' ') + ''','
                            else 
                                set @column_values += '''' + replace(substring(@values, charindex(@value_keys_start,@values,1) + len(@value_keys_start), charindex(@value_keys_end,@values,1) - (charindex(@value_keys_start,@values,1) + len(@value_keys_start))),'''','''''') + ''','
                        else if @QueryTypeID = 1 --update
                            if @collation_name is null and not (@type_name like '%date%' or @type_name like '%time%')
                                set @column_values += @column_name + '=' + substring(@values, charindex(@value_keys_start,@values,1) + len(@value_keys_start), charindex(@value_keys_end,@values,1) - (charindex(@value_keys_start,@values,1) + len(@value_keys_start))) + ','
                            else if @type_name like '%date%' or @type_name like '%time%'
                                set @column_values += @column_name + '=' + '''' + replace(substring(@values, charindex(@value_keys_start,@values,1) + len(@value_keys_start), charindex(@value_keys_end,@values,1) - (charindex(@value_keys_start,@values,1) + len(@value_keys_start))),'T',' ') + ''','
                            else 
                                set @column_values +=  @column_name + '=' + '''' + replace(substring(@values, charindex(@value_keys_start,@values,1) + len(@value_keys_start), charindex(@value_keys_end,@values,1) - (charindex(@value_keys_start,@values,1) + len(@value_keys_start))),'''','''''') + ''','
                    end
                end
                fetch next from columnCursor 
                into @column_name, @user_type_id, @max_length, @is_nullable;
            end
            if not cursor_status('local','columnCursor') <= -1
            begin
                close columnCursor;
                deallocate columnCursor;
            end
        
            if @QueryTypeID = 0 --insert
                set @Query += substring(@column_names,1,len(@column_names) - 1) + ')
                values (' + substring(@column_values,1,len(@column_values) - 1) + ')'
            else if @QueryTypeID = 1 --update or delete
                set @Query += substring(@column_values,1,len(@column_values) - 1) + ' where YourTablePKColumn = ' + @YourTablePKColumn
            else
                set @Query += ' where YourTablePKColumn = ' + @YourTablePKColumn
        
        end
        

        第 3 步:为您想要拥有事务日志的表创建触发器

            CREATE TRIGGER trg_MyTrigger ON YouTableName 
        AFTER INSERT, DELETE, UPDATE
        AS 
        BEGIN
            SET NOCOUNT ON;
        
            declare @TableName nvarchar(100) = 'YouTableName', @Query nvarchar(max), @QueryTypeID tinyint, @YourTablePKColumn nvarchar(30)
        
            if exists(select * from deleted) and exists(select * from inserted) 
            begin
                set @QueryTypeID = 1
                if not cursor_status('local','updatedCursor') <= -1
                begin
                    close updatedCursor;
                    deallocate updatedCursor;
                end
                declare updatedCursor cursor local for
                select cast(YourTablePKColumn as nvarchar(30)) from inserted;
                open updatedCursor;
                fetch next from updatedCursor 
                into @YourTablePKColumn;
                while @@FETCH_STATUS = 0
                begin
                    exec dbo.sp_CreateQueryLogs @Query = @Query output, @TableName = @TableName, @YourTablePKColumn = @YourTablePKColumn, @QueryTypeID = @QueryTypeID
                    insert into TransactionLogs
                             (Query, DateCreated)
                    values (@Query,getdate())
                    fetch next from updatedCursor 
                    into @YourTablePKColumn;
                end
                if not cursor_status('local','updatedCursor') <= -1
                begin
                    close updatedCursor;
                    deallocate updatedCursor;
                end
            end
            else if exists(select * from deleted) and not exists(select * from inserted)
            begin
                set @QueryTypeID = 2
                if not cursor_status('local','deletedCursor') <= -1
                begin
                    close deletedCursor;
                    deallocate deletedCursor;
                end
                declare deletedCursor cursor local for
                select cast(YourTablePKColumn as nvarchar(30)) from deleted;
                open deletedCursor;
                fetch next from deletedCursor 
                into @YourTablePKColumn;
                while @@FETCH_STATUS = 0
                begin
                    exec dbo.sp_CreateQueryLogs @Query = @Query output, @TableName = @TableName, @YourTablePKColumn = @YourTablePKColumn, @QueryTypeID = @QueryTypeID
                    insert into TransactionLogs
                             (Query, DateCreated)
                    values (@Query,getdate())
                    fetch next from deletedCursor 
                    into @YourTablePKColumn;
                end
                if not cursor_status('local','deletedCursor') <= -1
                begin
                    close deletedCursor;
                    deallocate deletedCursor;
                end
            end
            else
            begin
                set @QueryTypeID = 0
                if not cursor_status('local','insertedCursor') <= -1
                begin
                    close insertedCursor;
                    deallocate insertedCursor;
                end
                declare insertedCursor cursor local for
                select cast(YourTablePKColumn as nvarchar(30)) from inserted;
                open insertedCursor;
                fetch next from insertedCursor 
                into @YourTablePKColumn;
                while @@FETCH_STATUS = 0
                begin
                    exec dbo.sp_CreateQueryLogs @Query = @Query output, @TableName = @TableName, @YourTablePKColumn = @YourTablePKColumn, @QueryTypeID = @QueryTypeID
                    insert into TransactionLogs
                             (Query, DateCreated)
                    values (@Query,getdate())
                    fetch next from insertedCursor 
                    into @YourTablePKColumn;
                end
                if not cursor_status('local','insertedCursor') <= -1
                begin
                    close insertedCursor;
                    deallocate insertedCursor;
                end
            end
        
        END
        GO
        

        【讨论】:

          猜你喜欢
          • 2010-11-15
          • 2019-02-23
          • 2014-06-29
          • 2014-10-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多