zoukankan      html  css  js  c++  java
  • 转:使用fn_dblog解析SQL SERVER 数据库日志方法

    http://blog.itpub.net/8183550/viewspace-682907

    一直以来我都很困惑,不知道怎么解析SQL SERVER的日志,

    因为微软提供了fn_dblog(NULL,NULL)和DBCC LOG获取数据库日志的基本信息,但是都是二进制码,看不懂。最近终于成功解析了SQL SERVER LOG信息

    在fn_dblog(NULL,NULL)输出结果中,

    获取表名是AllocUnitName字段。

    具体获取方法:AllocUnitName like 'dbo.TEST%'

    操作类型是:Operation

    数据是:[RowLog Contents 0]字段内容

    如果是UPDATE操作:修改后数据存放在[RowLog Contents 1]字段内

    最基本3种操作类型:'LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW'

    具体解析代码如下:

    --解析日志
    create function dbo.f_splitBinary(@s varbinary(8000))
    returns @t table(id int identity(1,1),Value binary(1))
    as
    begin
    declare @i int,@im int
    select @i=1,@im=datalength(@s)
    while @i<=@im
    begin
      insert into @t select substring(@s,@i,1) 
      set @i=@i+1
    end
    return
    end

    GO

    create function dbo.f_reverseBinary(@s varbinary(128))
    returns varbinary(128)
    as
    begin
    declare @r varbinary(128)
    set @r=0x
    select @r=@r+Value from dbo.f_splitBinary(@s) a order by id desc
    return @r
    end

    GO
     
     
     
    create proc [dbo].[p_getLog](@TableName sysname,@c int=100)
    /*
    解析日志
    胡冰
    时间:2010年12月30日
    工作单位:www.5173.com

    sample:  p_getLog 'tablename';

    */
    as
    set nocount on
    declare @s varbinary(8000),@s1 varbinary(8000),@str varchar(8000),@str1 varchar(8000),@lb int,@le int,@operation varchar(128)
    declare @i int,@lib int,@lie int,@ib int,@ie int,@lenVar int,@columnname sysname,@length int,@columntype varchar(32),@prec int,@scale int
    declare @TUVLength int,@vc int,@tc int,@bitAdd int,@bitCount int,@count int

    select b.name,b.length,c.name typename,b.colid,b.xprec,b.xscale,
        case when c.name not like '%var%' and c.name not in ('xml','text','image') then 1 else 2 end p,row_number() over(partition by 
        case when c.name not like '%var%' and c.name not in ('xml','text','image') then 1 else 2 end order by colid) pid
    into #t
        from sysobjects a inner join syscolumns b on a.id=b.id inner join systypes c on b.xtype=c.xusertype
        where a.name=@TableName order by b.colid

    SELECT top(@c) Operation,[RowLog Contents 0],[RowLog Contents 1],[RowLog Contents 2],[RowLog Contents 3],[Log Record],id=identity(int,1,1) into #t1
        from::fn_dblog (null, null) 
        where AllocUnitName like 'dbo.'+@TableName+'%'and
        Operation in('LOP_INSERT_ROWS','LOP_DELETE_ROWS','LOP_MODIFY_ROW' )
        AND Context not in ('LCX_IAM','LCX_PFS')
         order by [Current LSN] desc

    select @tc=count(*) from #t

    select @lb=min(id),@le=max(id) from #t1
    while @lb<=@le
    begin
        select @operation=Operation,@s=[RowLog Contents 0],@s1=[RowLog Contents 1] from #t1 where id=@lb AND [RowLog Contents 1] IS NOT NULL
        set @TUVLength=convert(int,dbo.f_reverseBinary(substring(@s,3,2)))+3
        select @i=5,@str='',@vc=0,@bitCount=0
        select @lib=min(pid),@lie=max(pid) from #t where p=1 
        while @lib<=@lie
        begin
            select @columnname=name,@length=length,@columntype=typename,@prec=xprec,@scale=xscale,@vc=colid-1 from #t where p=1 and pid=@lib
    --        if @columntype<>'bit'
    --            print rtrim(@i)+'->'+rtrim(@length)

            if dbo.f_reverseBinary(substring(@s,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0 
            begin
                if @columntype<>'bit'
                    select @str=@str+@columnname+'=NULL,',@i=@i+@length
                else
                begin
                    select @str=@str+@columnname+'=NULL,'
                    set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
                    set @bitCount = (@bitCount + 1)%8    
                    set @i=@i+case @bitCount when 1 then 1 else 0 end
    --                print rtrim(@bitAdd)+'->'+rtrim(@length)
                end                
            end
            else if @columntype='char'
                select @str=@str+@columnname+'='+convert(varchar(256),substring(@s,@i,@length))+',',@i=@i+@length
            else if @columntype='nchar'
                select @str=@str+@columnname+'='+convert(nvarchar(256),substring(@s,@i,@length))+',',@i=@i+@length
            else if @columntype='datetime'
                select @str=@str+@columnname+'='+convert(varchar,dateadd(second,convert(int,dbo.f_reverseBinary(substring(@s,@i,4)))/300
                    ,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s,@i+4,4))),'1900-01-01')),120)+',',@i=@i+8
            else if @columntype='smalldatetime'
                select @str=@str+@columnname+'='+convert(varchar,dateadd(minute,convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
                    ,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s,@i+2,2))),'1900-01-01')),120)+',',@i=@i+4
            else if @columntype='int'
                select @str=@str+@columnname+'='+rtrim(convert(int,dbo.f_reverseBinary(substring(@s,@i,4))))+',',@i=@i+4
            else if @columntype='decimal'
                select @str=@str+@columnname+'=DECIMAL,',@i=@i+@length
            else if @columntype='bit'
            begin
                set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
                set @bitCount = (@bitCount + 1)%8
                select @str=@str+@columnname+'='+rtrim(convert(bit,substring(@s,@bitAdd,1)&power(2,case @bitCount when 0 then 8 else @bitCount end-1)))+','
                    ,@i=@i+case @bitCount when 1 then 1 else 0 end
    --            print rtrim(@bitAdd)+'->'+rtrim(@length)
            end
            set @lib=@lib+1
        end
        set @i=convert(int,dbo.f_reverseBinary(substring(@s,3,2)))+4+((@tc-1)/8)
        set @lenVar=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
        set @i=@i+2
        set @ib=@i + @lenVar*2
        set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
        set @count=0
        select @lib=min(pid),@lie=max(pid) from #t where p=2 
        while @lib<=@lie
        begin
    --        print rtrim(@ib)+'->'+rtrim(@ie)
            select @columnname=name,@length=length,@columntype=typename,@vc=colid-1 from #t where p=2 and pid=@lib
            if dbo.f_reverseBinary(substring(@s,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0 
            begin
                select @str=@str+@columnname+'=NULL,'
                select @ib=@ie+1,@i=@i+2
                if @count<@lenVar
                    set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
            end
            else if @columntype='varchar'
            begin
                select @str=@str+@columnname+'='+convert(varchar(256),substring(@s,@ib,@ie-@ib+1))+','
                select @ib=@ie+1,@i=@i+2
                set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
            end
            else if @columntype='nvarchar'
            begin
                select @str=@str+@columnname+'='+convert(nvarchar(256),substring(@s,@ib,@ie-@ib+1))+','
                select @ib=@ie+1,@i=@i+2
                set @ie=convert(int,dbo.f_reverseBinary(substring(@s,@i,2)))
            end
            set @count=@count+1
            set @lib=@lib+1
        end
        set @str=left(@str,len(@str)-1)
        
         IF @operation ='LOP_MODIFY_ROW'  
         BEGIN
              set @TUVLength=convert(int,dbo.f_reverseBinary(substring(@s1,3,2)))+3
       select @i=5,@str1='',@vc=0,@bitCount=0
       select @lib=min(pid),@lie=max(pid) from #t where p=1 
       while @lib<=@lie
       begin
        select @columnname=name,@length=length,@columntype=typename,@prec=xprec,@scale=xscale,@vc=colid-1 from #t where p=1 and pid=@lib
      --        if @columntype<>'bit'
      --            print rtrim(@i)+'->'+rtrim(@length)

        if dbo.f_reverseBinary(substring(@s1,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0 
        begin
         if @columntype<>'bit'
          select @str1=@str1+@columnname+'=NULL,',@i=@i+@length
         else
         begin
          select @str1=@str1+@columnname+'=NULL,'
          set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
          set @bitCount = (@bitCount + 1)%8    
          set @i=@i+case @bitCount when 1 then 1 else 0 end
      --                print rtrim(@bitAdd)+'->'+rtrim(@length)
         end                
        end
        else if @columntype='char'
         select @str1=@str1+@columnname+'='+convert(varchar(256),substring(@s1,@i,@length))+',',@i=@i+@length
        else if @columntype='nchar'
         select @str1=@str1+@columnname+'='+convert(nvarchar(256),substring(@s1,@i,@length))+',',@i=@i+@length
        else if @columntype='datetime'
         select @str1=@str1+@columnname+'='+convert(varchar,dateadd(second,convert(int,dbo.f_reverseBinary(substring(@s1,@i,4)))/300
          ,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s1,@i+4,4))),'1900-01-01')),120)+',',@i=@i+8
        else if @columntype='smalldatetime'
         select @str1=@str1+@columnname+'='+convert(varchar,dateadd(minute,convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
          ,dateadd(day,convert(int,dbo.f_reverseBinary(substring(@s1,@i+2,2))),'1900-01-01')),120)+',',@i=@i+4
        else if @columntype='int'
         select @str1=@str1+@columnname+'='+rtrim(convert(int,dbo.f_reverseBinary(substring(@s1,@i,4))))+',',@i=@i+4
        else if @columntype='decimal'
         select @str1=@str1+@columnname+'=DECIMAL,',@i=@i+@length
        else if @columntype='bit'
        begin
         set @bitAdd = case when @bitCount=0 then @i else @bitAdd end
         set @bitCount = (@bitCount + 1)%8
         select @str1=@str1+@columnname+'='+rtrim(convert(bit,substring(@s1,@bitAdd,1)&power(2,case @bitCount when 0 then 8 else @bitCount end-1)))+','
          ,@i=@i+case @bitCount when 1 then 1 else 0 end
      --            print rtrim(@bitAdd)+'->'+rtrim(@length)
        end
        set @lib=@lib+1
       end
       set @i=convert(int,dbo.f_reverseBinary(substring(@s1,3,2)))+4+((@tc-1)/8)
       set @lenVar=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
       set @i=@i+2
       set @ib=@i + @lenVar*2
       set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
       set @count=0
       select @lib=min(pid),@lie=max(pid) from #t where p=2 
       while @lib<=@lie
       begin
      --        print rtrim(@ib)+'->'+rtrim(@ie)
        select @columnname=name,@length=length,@columntype=typename,@vc=colid-1 from #t where p=2 and pid=@lib
        if dbo.f_reverseBinary(substring(@s1,@TUVLength,1+((@tc-1)/8))) & power(2,@vc) <> 0 
        begin
         select @str1=@str1+@columnname+'=NULL,'
         select @ib=@ie+1,@i=@i+2
         if @count<@lenVar
          set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
        end
        else if @columntype='varchar'
        begin
         select @str1=@str1+@columnname+'='+convert(varchar(256),substring(@s1,@ib,@ie-@ib+1))+','
         select @ib=@ie+1,@i=@i+2
         set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
        end
        else if @columntype='nvarchar'
        begin
         select @str1=@str1+@columnname+'='+convert(nvarchar(256),substring(@s1,@ib,@ie-@ib+1))+','
         select @ib=@ie+1,@i=@i+2
         set @ie=convert(int,dbo.f_reverseBinary(substring(@s1,@i,2)))
        end
        set @count=@count+1
        set @lib=@lib+1
       end
       set @str1=left(@str1,len(@str1)-1)

         END
        
        IF @operation ='LOP_MODIFY_ROW'  
        BEGIN
         print   @operation+'修改前值: '+@str
         
         print   @operation+'修改后值:'+@str1
         
        END
        ELSE
     BEGIN
      print @operation+':'+@str
     END
        
        
        
        
        set @lb=@lb+1
    END

    drop table #t,#t1

    GO

    因为目前解析SQL 2008会有很多错误 

  • 相关阅读:
    接口测试
    jmeter直连数据库
    登录功能的测试用例设计
    oracle 同义词synonym
    oracle常用函数
    python环境搭建--pycharm的安装及使用
    JavaScript数组函数
    JavaScript:var、let、作用域
    HTML入门到精通(带你全面避坑)
    使用VirtualBox安装CentOS7
  • 原文地址:https://www.cnblogs.com/zcm123/p/7456840.html
Copyright © 2011-2022 走看看