1. 程式人生 > >sql 查詢何時何人刪除何條記錄的儲存過程

sql 查詢何時何人刪除何條記錄的儲存過程

sql log blog

Recover_Deleted_Data_With_UID_Date_Time_Proc

IF OBJECT_ID('Recover_Deleted_Data_With_UID_Date_Time_Proc') IS NOT NULL
	DROP PROC Recover_Deleted_Data_With_UID_Date_Time_Proc;
GO


-- http://raresql.com/2012/10/24/sql-server-how-to-find-who-deleted-what-records-at-what-time/
-- Script Name: Recover_Deleted_Data_With_UID_Date_Time_Proc
-- Script Type : Recovery Procedure 
-- Develop By: Muhammad Imran
-- Date Created: 24 Oct 2012
-- Modify Date: 
-- Version    : 1.3
-- Notes      : 
-- 1.修改不傳入DB Name改取自DB_NAME()
-- 2.修改 sysname DataType to nvarchar(128)
-- 3.使用SUSER_SNAME 代替 sysusers join 找出操作人員
-- 4.修正在區分大小寫的DB中,會發生的錯誤
-- 5.加入判斷,如下是透過SSMS的Edit Row時,刪除的[Transaction Name] 會用 user_transaction [Transaction Name] IN ('DELETE', 'user_transaction')
-- ************************************************************************
-- v 1.3. Release Note
-- 1.修改Server的定序跟Database不同,會有 Cannot resolve the collation conflict between 的錯誤
-- Example: EXEC Recover_Deleted_Data_With_UID_Date_Time_Proc  'dbo.tbl_Sample'
CREATE PROCEDURE Recover_Deleted_Data_With_UID_Date_Time_Proc
    @SchemaName_n_TableName NVARCHAR(MAX) ,
    @Date_From DATETIME = '1900/01/01' ,
    @Date_To DATETIME = '9999/12/31'
AS 
    DECLARE @RowLogContents VARBINARY(8000)
    DECLARE @TransactionID NVARCHAR(MAX)
    DECLARE @AllocUnitID BIGINT
    DECLARE @AllocUnitName NVARCHAR(MAX)
    DECLARE @SQL NVARCHAR(MAX)
    DECLARE @Compatibility_Level INT
	DECLARE @Database_Name NVARCHAR(MAX	)
 
	SET @Database_Name = DB_NAME()
    SELECT  @Compatibility_Level = dtb.compatibility_level
    FROM    master.sys.databases AS dtb
    WHERE   dtb.name = @Database_Name
 
    IF ISNULL(@Compatibility_Level, 0) <= 80 
        BEGIN
            RAISERROR('The compatibility level should be equal to or greater SQL SERVER 2005 (90)',16,1)
            RETURN
        END
 
    IF ( SELECT COUNT(*)
         FROM   INFORMATION_SCHEMA.TABLES
         WHERE  [TABLE_SCHEMA] + '.' + [TABLE_NAME] = @SchemaName_n_TableName
       ) = 0 
        BEGIN
            RAISERROR('Could not found the table in the defined database',16,1)
            RETURN
        END
 
    DECLARE @bitTable TABLE
        (
          [ID] INT ,
          [Bitvalue] INT
        )
--Create table to set the bit position of one byte.
 
    INSERT  INTO @bitTable
            SELECT  0 ,
                    2
            UNION ALL
            SELECT  1 ,
                    2
            UNION ALL
            SELECT  2 ,
                    4
            UNION ALL
            SELECT  3 ,
                    8
            UNION ALL
            SELECT  4 ,
                    16
            UNION ALL
            SELECT  5 ,
                    32
            UNION ALL
            SELECT  6 ,
                    64
            UNION ALL
            SELECT  7 ,
                    128
 
--Create table to collect the row data.
    DECLARE @DeletedRecords TABLE
        (
          [Row ID] INT IDENTITY(1, 1) ,
          [RowLogContents] VARBINARY(8000) ,
          [AllocUnitId] BIGINT ,
          [Transaction ID] NVARCHAR(MAX) ,
          [FixedLengthData] SMALLINT ,
          [TotalNoOfCols] SMALLINT ,
          [NullBitMapLength] SMALLINT ,
          [NullBytes] VARBINARY(8000) ,
          [TotalNoofVarCols] SMALLINT ,
          [ColumnOffsetArray] VARBINARY(8000) ,
          [VarColumnStart] SMALLINT ,
          [Slot ID] INT ,
          [NullBitMap] VARCHAR(MAX)
        )
--Create a common table expression to get all the row data plus how many bytes we have for each row.
;
    WITH    RowData
              AS ( SELECT   [RowLog Contents 0] AS [RowLogContents] ,
                            [AllocUnitID] AS [AllocUnitID] ,
                            [Transaction ID] AS [Transaction ID]  
 
--[Fixed Length Data] = Substring (RowLog content 0, Status Bit A+ Status Bit B + 1,2 bytes)
                            ,
                            CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
                                                              2 + 1, 2)))) AS [FixedLengthData]  
[email protected]
-- [TotalnoOfCols] = Substring (RowLog content 0, [Fixed Length Data] + 1,2 bytes) , CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) AS [TotalNoOfCols] --[NullBitMapLength]=ceiling([Total No of Columns] /8.0) , CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) / 8.0)) AS [NullBitMapLength] --[Null Bytes] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [NullBitMapLength] ) , SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3, CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) / 8.0))) AS [NullBytes] --[TotalNoofVarCols] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 ) , ( CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN ( 0x10, 0x30, 0x70 ) THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) / 8.0)), 2)))) ELSE NULL END ) AS [TotalNoofVarCols] --[ColumnOffsetArray]= Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 , [TotalNoofVarCols]*2 ) , ( CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN ( 0x10, 0x30, 0x70 ) THEN SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) / 8.0)) + 2, ( CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN ( 0x10, 0x30, 0x70 ) THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) / 8.0)), 2)))) ELSE NULL END ) * 2) ELSE NULL END ) AS [ColumnOffsetArray] -- Variable column Start = Status Bit A+ Status Bit B + [Fixed Length Data] + [Null Bitmap length] + 2+([TotalNoofVarCols]*2) , CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN ( 0x10, 0x30, 0x70 ) THEN ( CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 4 + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) / 8.0)) + ( ( CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN ( 0x10, 0x30, 0x70 ) THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) / 8.0)), 2)))) ELSE NULL END ) * 2 ) ) ELSE NULL END AS [VarColumnStart] , [Slot ID] FROM sys.fn_dblog(NULL, NULL) WHERE AllocUnitId IN ( SELECT [Allocation_unit_id] FROM sys.allocation_units allocunits INNER JOIN sys.partitions partitions ON ( allocunits.type IN ( 1, 3 ) AND partitions.hobt_id = allocunits.container_id ) OR ( allocunits.type = 2 AND partitions.partition_id = allocunits.container_id ) WHERE object_id = OBJECT_ID('' + @SchemaName_n_TableName + '') ) AND Context IN ( 'LCX_MARK_AS_GHOST', 'LCX_HEAP' ) AND Operation IN ( 'LOP_DELETE_ROWS' ) AND SUBSTRING([RowLog Contents 0], 1, 1) IN ( 0x10, 0x30, 0x70 ) /*Use this subquery to filter the date*/ AND [TRANSACTION ID] IN ( SELECT DISTINCT [TRANSACTION ID] FROM sys.fn_dblog(NULL, NULL) WHERE Context IN ( 'LCX_NULL' ) AND Operation IN ( 'LOP_BEGIN_XACT' ) AND [Transaction Name] IN ('DELETE', 'user_transaction') AND CONVERT(NVARCHAR(11), [Begin Time]) BETWEEN @Date_From AND @Date_To) ), --Use this technique to repeate the row till the no of bytes of the row. N1 ( n ) AS ( SELECT 1 UNION ALL SELECT 1 ), N2 ( n ) AS ( SELECT 1 FROM N1 AS X , N1 AS Y ), N3 ( n ) AS ( SELECT 1 FROM N2 AS X , N2 AS Y ), N4 ( n ) AS ( SELECT ROW_NUMBER() OVER ( ORDER BY X.n ) FROM N3 AS X , N3 AS Y ) INSERT INTO @DeletedRecords SELECT RowLogContents , [AllocUnitID] , [Transaction ID] , [FixedLengthData] , [TotalNoOfCols] , [NullBitMapLength] , [NullBytes] , [TotalNoofVarCols] , [ColumnOffsetArray] , [VarColumnStart] , [Slot ID] ---Get the Null value against each column (1 means null zero means not null) , [NullBitMap] = ( REPLACE(STUFF(( SELECT ',' + ( CASE WHEN [ID] = 0 THEN CONVERT(NVARCHAR(1), ( SUBSTRING(NullBytes, n, 1) % 2 )) ELSE CONVERT(NVARCHAR(1), ( ( SUBSTRING(NullBytes, n, 1) / [Bitvalue] ) % 2 )) END ) --as [NullBitMap] FROM N4 AS Nums JOIN RowData AS C ON n <= NullBitMapLength CROSS JOIN @bitTable WHERE C.[RowLogContents] = D.[RowLogContents] ORDER BY [RowLogContents] , n ASC FOR XML PATH('') ), 1, 1, ''), ',', '') ) FROM RowData D IF ( SELECT COUNT(*) FROM @DeletedRecords ) = 0 BEGIN RAISERROR('There is no data in the log as per the search criteria',16,1) RETURN END DECLARE @ColumnNameAndData TABLE ( [Transaction ID] VARCHAR(100) , [Row ID] INT , [RowLogContents] VARBINARY(MAX) , [NAME] nvarchar(128) , [nullbit] SMALLINT , [leaf_offset] SMALLINT , [length] SMALLINT , [system_type_id] TINYINT , [bitpos] TINYINT , [xprec] TINYINT , [xscale] TINYINT , [is_null] INT , [Column value Size] INT , [Column Length] INT , [hex_Value] VARBINARY(MAX) , [Slot ID] INT , [Update] INT ) --Create common table expression and join it with the rowdata table -- to get each column details /*This part is for variable data columns*/
[email protected]
, --(col.columnOffValue - col.columnLength) + 1, --col.columnLength --) INSERT INTO @ColumnNameAndData SELECT [Transaction ID] , [Row ID] , RowLogContents , NAME , cols.leaf_null_bit AS nullbit , leaf_offset , ISNULL(syscolumns.length, cols.max_length) AS [length] , cols.system_type_id , cols.leaf_bit_position AS bitpos , ISNULL(syscolumns.xprec, cols.precision) AS xprec , ISNULL(syscolumns.xscale, cols.scale) AS xscale , SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) AS is_null , ( CASE WHEN leaf_offset < 1 AND SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) = 0 THEN ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - POWER(2, 15) ELSE CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) END ) END ) AS [Column value Size] , ( CASE WHEN leaf_offset < 1 AND SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) = 0 THEN ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) < 30000 THEN ( CASE WHEN [system_type_id] IN ( 35, 34, 99 ) THEN 16 ELSE 24 END ) WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) > 30000 THEN ( CASE WHEN [system_type_id] IN ( 35, 34, 99 ) THEN 16 ELSE 24 END ) --24 WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) < 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) < 30000 THEN ( CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) ) WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) < 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) > 30000 THEN POWER(2, 15) + CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) END ) END ) AS [Column Length] , ( CASE WHEN SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) = 1 THEN NULL ELSE SUBSTRING(RowLogContents, ( ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - POWER(2, 15) ELSE CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) END ) - ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) < 30000 THEN ( CASE WHEN [system_type_id] IN ( 35, 34, 99 ) THEN 16 ELSE 24 END ) --24 WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) > 30000 THEN ( CASE WHEN [system_type_id] IN ( 35, 34, 99 ) THEN 16 ELSE 24 END ) --24 WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) < 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) < 30000 THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) < 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) > 30000 THEN POWER(2, 15) + CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) END ) ) + 1, ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) < 30000 THEN ( CASE WHEN [system_type_id] IN ( 35, 34, 99 ) THEN 16 ELSE 24 END ) --24 WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) > 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) > 30000 THEN ( CASE WHEN [system_type_id] IN ( 35, 34, 99 ) THEN 16 ELSE 24 END ) --24 WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) < 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) < 30000 THEN ABS(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart])) WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) < 30000 AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) > 30000 THEN POWER(2, 15) + CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * leaf_offset * -1 ) - 1, 2)))) - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray], ( 2 * ( ( leaf_offset * -1 ) - 1 ) ) - 1, 2)))), 0), [VarColumnStart]) END )) END ) AS hex_Value , [Slot ID] , 0 FROM @DeletedRecords A INNER JOIN sys.allocation_units allocunits ON A.[AllocUnitId] = allocunits.[Allocation_Unit_Id] INNER JOIN sys.partitions partitions ON ( allocunits.type IN ( 1, 3 ) AND partitions.hobt_id = allocunits.container_id ) OR ( allocunits.type = 2 AND partitions.partition_id = allocunits.container_id ) INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id AND syscolumns.colid = cols.partition_column_id WHERE leaf_offset < 0 UNION /*This part is for fixed data columns*/ SELECT [Transaction ID] , [Row ID] , RowLogContents , NAME , cols.leaf_null_bit AS nullbit , leaf_offset , ISNULL(syscolumns.length, cols.max_length) AS [length] , cols.system_type_id , cols.leaf_bit_position AS bitpos , ISNULL(syscolumns.xprec, cols.precision) AS xprec , ISNULL(syscolumns.xscale, cols.scale) AS xscale , SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) AS is_null , ( SELECT TOP 1 ISNULL(SUM(CASE WHEN C.leaf_offset > 1 THEN max_length ELSE 0 END), 0) FROM sys.system_internals_partition_columns C WHERE cols.partition_id = C.partition_id AND C.leaf_null_bit < cols.leaf_null_bit ) + 5 AS [Column value Size] , syscolumns.length AS [Column Length] , CASE WHEN SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) = 1 THEN NULL ELSE SUBSTRING(RowLogContents, ( SELECT TOP 1 ISNULL(SUM(CASE WHEN C.leaf_offset > 1 AND C.leaf_bit_position = 0 THEN max_length ELSE 0 END), 0) FROM sys.system_internals_partition_columns C WHERE cols.partition_id = C.partition_id AND C.leaf_null_bit < cols.leaf_null_bit ) + 5, syscolumns.length) END AS hex_Value , [Slot ID] , 0 FROM @DeletedRecords A INNER JOIN sys.allocation_units allocunits ON A.[AllocUnitId] = allocunits.[Allocation_Unit_Id] INNER JOIN sys.partitions partitions ON ( allocunits.type IN ( 1, 3 ) AND partitions.hobt_id = allocunits.container_id ) OR ( allocunits.type = 2 AND partitions.partition_id = allocunits.container_id ) INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id AND syscolumns.colid = cols.partition_column_id WHERE leaf_offset > 0 ORDER BY nullbit DECLARE @BitColumnByte AS INT SELECT @BitColumnByte = CONVERT(INT, CEILING(COUNT(*) / 8.0)) FROM @ColumnNameAndData WHERE [system_type_id] = 104; WITH N1 ( n ) AS ( SELECT 1 UNION ALL SELECT 1 ), N2 ( n ) AS ( SELECT 1 FROM N1 AS X , N1 AS Y ), N3 ( n ) AS ( SELECT 1 FROM N2 AS X , N2 AS Y ), N4 ( n ) AS ( SELECT ROW_NUMBER() OVER ( ORDER BY X.n ) FROM N3 AS X , N3 AS Y ), CTE AS ( SELECT RowLogContents , [nullbit] , [BitMap] = CONVERT(VARBINARY(1), CONVERT(INT, SUBSTRING(( REPLACE(STUFF(( SELECT ',' + ( CASE WHEN [ID] = 0 THEN CONVERT(NVARCHAR(1), ( SUBSTRING(hex_Value, n, 1) % 2 )) ELSE CONVERT(NVARCHAR(1), ( ( SUBSTRING(hex_Value, n, 1) / [Bitvalue] ) % 2 )) END ) --as [NullBitMap] FROM N4 AS Nums JOIN @ColumnNameAndData AS C ON n <= @BitColumnByte AND [system_type_id] = 104 AND bitpos = 0 CROSS JOIN @bitTable WHERE C.[RowLogContents] = D.[RowLogContents] ORDER BY [RowLogContents] , n ASC FOR XML PATH('') ), 1, 1, ''), ',', '') ), bitpos + 1, 1))) FROM @ColumnNameAndData D WHERE [system_type_id] = 104 ) UPDATE A SET [hex_Value] = [BitMap] FROM @ColumnNameAndData A INNER JOIN CTE B ON A.[RowLogContents] = B.[RowLogContents] AND A.[nullbit] = B.[nullbit] /**************Check for BLOB DATA TYPES******************************/ DECLARE @Fileid INT DECLARE @Pageid INT DECLARE @Slotid INT DECLARE @CurrentLSN INT DECLARE @LinkID INT DECLARE @Context VARCHAR(50) DECLARE @ConsolidatedPageID VARCHAR(MAX) DECLARE @LCX_TEXT_MIX VARBINARY(MAX) DECLARE @temppagedata TABLE ( [ParentObject] nvarchar(128) , [Object] nvarchar(128) , [Field] nvarchar(128) , [Value] nvarchar(128) ) DECLARE @pagedata TABLE ( [Page ID] nvarchar(128) , [FILE IDS] INT , [PAGE IDS] INT , [AllocUnitId] BIGINT , [ParentObject] nvarchar(128) , [Object] nvarchar(128) , [Field] nvarchar(128) , [Value] nvarchar(128) ) DECLARE @ModifiedRawData TABLE ( [ID] INT IDENTITY(1, 1) , [PAGE ID] VARCHAR(MAX) , [FILE IDS] INT , [PAGE IDS] INT , [Slot ID] INT , [AllocUnitId] BIGINT , [RowLog Contents 0_var] VARCHAR(MAX) , [RowLog Length] VARCHAR(50) , [RowLog Len] INT , [RowLog Contents 0] VARBINARY(MAX) , [LINK ID] INT DEFAULT ( 0 ) , [Update] INT ) DECLARE Page_Data_Cursor CURSOR FOR /*We need to filter LOP_MODIFY_ROW,LOP_MODIFY_COLUMNS from log for deleted records of BLOB data type& Get its Slot No, Page ID & AllocUnit ID*/ SELECT LTRIM(RTRIM(REPLACE([Description], 'Deallocated', ''))) AS [PAGE ID] , [Slot ID] , [AllocUnitId] , NULL AS [RowLog Contents 0] , NULL AS [RowLog Contents 0] , Context FROM sys.fn_dblog(NULL, NULL) WHERE AllocUnitId IN ( SELECT [Allocation_unit_id] FROM sys.allocation_units allocunits INNER JOIN sys.partitions partitions ON ( allocunits.type IN ( 1, 3 ) AND partitions.hobt_id = allocunits.container_id ) OR ( allocunits.type = 2 AND partitions.partition_id = allocunits.container_id ) WHERE object_id = OBJECT_ID('' + @SchemaName_n_TableName + '') ) AND Operation IN ( 'LOP_MODIFY_ROW' ) AND [Context] IN ( 'LCX_PFS' ) AND Description LIKE '%Deallocated%' /*Use this subquery to filter the date*/ AND [TRANSACTION ID] IN ( SELECT DISTINCT [TRANSACTION ID] FROM sys.fn_dblog(NULL, NULL) WHERE Context IN ( 'LCX_NULL' ) AND Operation IN ( 'LOP_BEGIN_XACT' ) AND [Transaction Name] IN ('DELETE', 'user_transaction') AND CONVERT(NVARCHAR(11), [Begin Time]) BETWEEN @Date_From AND @Date_To ) GROUP BY [Description] , [Slot ID] , [AllocUnitId] , Context UNION SELECT [PAGE ID] , [Slot ID] , [AllocUnitId] , SUBSTRING([RowLog Contents 0], 15, LEN([RowLog Contents 0])) AS [RowLog Contents 0] , CONVERT(INT, SUBSTRING([RowLog Contents 0], 7, 2)) , Context --,CAST(RIGHT([Current LSN],4) AS INT) AS [Current LSN] FROM sys.fn_dblog(NULL, NULL) WHERE AllocUnitId IN ( SELECT [Allocation_unit_id] FROM sys.allocation_units allocunits INNER JOIN sys.partitions partitions ON ( allocunits.type IN ( 1, 3 ) AND partitions.hobt_id = allocunits.container_id ) OR ( allocunits.type = 2 AND partitions.partition_id = allocunits.container_id ) WHERE object_id = OBJECT_ID('' + @SchemaName_n_TableName + '') ) AND Context IN ( 'LCX_TEXT_MIX' ) AND Operation IN ( 'LOP_DELETE_ROWS' ) /*Use this subquery to filter the date*/ AND [TRANSACTION ID] IN ( SELECT DISTINCT [TRANSACTION ID] FROM sys.fn_dblog(NULL, NULL) WHERE Context IN ( 'LCX_NULL' ) AND Operation IN ( 'LOP_BEGIN_XACT' ) AND [Transaction Name] IN ('DELETE', 'user_transaction') AND CONVERT(NVARCHAR(11), [Begin Time]) BETWEEN @Date_From AND @Date_To ) /****************************************/ OPEN Page_Data_Cursor FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid, @AllocUnitID, @LCX_TEXT_MIX, @LinkID, @Context WHILE @@FETCH_STATUS = 0 BEGIN DECLARE @hex_pageid AS VARCHAR(MAX) /*Page ID contains File Number and page number It looks like 0001:00000130. In this example 0001 is file Number & 00000130 is Page Number & These numbers are in Hex format*/ SET @Fileid = SUBSTRING(@ConsolidatedPageID, 0, CHARINDEX(':', @ConsolidatedPageID)) -- Seperate File ID from Page ID SET @hex_pageid = '0x' + SUBSTRING(@ConsolidatedPageID, CHARINDEX(':', @ConsolidatedPageID) + 1, LEN(@ConsolidatedPageID)) ---Seperate the page ID SELECT @Pageid = CONVERT(INT, CAST('' AS XML).value('xs:hexBinary(substring(sql:variable("@hex_pageid"),sql:column("t.pos")) )', 'varbinary(max)')) -- Convert Page ID from hex to integer FROM ( SELECT CASE SUBSTRING(@hex_pageid, 1, 2) WHEN '0x' THEN 3 ELSE 0 END ) AS t ( pos ) IF @Context = 'LCX_PFS' BEGIN DELETE @temppagedata INSERT INTO @temppagedata EXEC ( 'DBCC PAGE(' + @Database_Name + ', ' + @Fileid + ', ' + @Pageid + ', 1) with tableresults,no_infomsgs;' ); INSERT INTO @pagedata SELECT @ConsolidatedPageID , @Fileid , @Pageid , @AllocUnitID , [ParentObject] , [Object] , [Field] , [Value] FROM @temppagedata END ELSE IF @Context = 'LCX_TEXT_MIX' BEGIN INSERT INTO @ModifiedRawData SELECT @ConsolidatedPageID , @Fileid , @Pageid , @Slotid , @AllocUnitID , NULL , 0 , CONVERT(INT, CONVERT(VARBINARY, REVERSE(SUBSTRING(@LCX_TEXT_MIX, 11, 2)))) , @LCX_TEXT_MIX , @LinkID , 0 END FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid, @AllocUnitID, @LCX_TEXT_MIX, @LinkID, @Context END CLOSE Page_Data_Cursor DEALLOCATE Page_Data_Cursor DECLARE @Newhexstring VARCHAR(MAX); --The data is in multiple rows in the page, so we need to convert it into one row as a single hex value. --This hex value is in string format INSERT INTO @ModifiedRawData ( [PAGE ID] , [FILE IDS] , [PAGE IDS] , [Slot ID] , [AllocUnitId] , [RowLog Contents 0_var] , [RowLog Length] ) SELECT [Page ID] , [FILE IDS] , [PAGE IDS] , SUBSTRING([ParentObject], CHARINDEX('Slot', [ParentObject]) + 4, ( CHARINDEX('Offset', [ParentObject]) - ( CHARINDEX('Slot', [ParentObject]) + 4 ) ) - 2) AS [Slot ID] , [AllocUnitId] , SUBSTRING(( SELECT REPLACE(STUFF(( SELECT REPLACE(SUBSTRING([Value], CHARINDEX(':', [Value]) + 1, CHARINDEX('†', [Value]) - CHARINDEX(':', [Value])), '†', '') FROM @pagedata C WHERE B.[Page ID] = C.[Page ID] AND SUBSTRING(B.[ParentObject], CHARINDEX('Slot', B.[ParentObject]) + 4, ( CHARINDEX('Offset', B.[ParentObject]) - ( CHARINDEX('Slot', B.[ParentObject]) + 4 ) )) = SUBSTRING(C.[ParentObject], CHARINDEX('Slot', C.[ParentObject]) + 4, ( CHARINDEX('Offset', C.[ParentObject]) - ( CHARINDEX('Slot', C.[ParentObject]) + 4 ) )) AND [Object] LIKE '%Memory Dump%' ORDER BY '0x' + LEFT([Value], CHARINDEX(':', [Value]) - 1) FOR XML PATH('') ), 1, 1, ''), ' ', '') ), 1, 20000) AS [Value] , SUBSTRING(( SELECT '0x' + REPLACE(STUFF(( SELECT REPLACE(SUBSTRING([Value], CHARINDEX(':', [Value]) + 1, CHARINDEX('†', [Value]) - CHARINDEX(':', [Value])), '†', '') FROM @pagedata C WHERE B.[Page ID] = C.[Page ID] AND SUBSTRING(B.[ParentObject], CHARINDEX('Slot', B.[ParentObject]) + 4, ( CHARINDEX('Offset', B.[ParentObject]) - ( CHARINDEX('Slot', B.[ParentObject]) + 4 ) )) = SUBSTRING(C.[ParentObject], CHARINDEX('Slot', C.[ParentObject]) + 4, ( CHARINDEX('Offset', C.[ParentObject]) - ( CHARINDEX('Slot', C.[ParentObject]) + 4 ) )) AND [Object] LIKE '%Memory Dump%' ORDER BY '0x' + LEFT([Value], CHARINDEX(':', [Value]) - 1) FOR XML PATH('') ), 1, 1, ''), ' ', '') ), 7, 4) AS [Length] FROM @pagedata B WHERE [Object] LIKE '%Memory Dump%' GROUP BY [Page ID] , [FILE IDS] , [PAGE IDS] , [ParentObject] , [AllocUnitId]--,[Current LSN] ORDER BY [Slot ID] UPDATE @ModifiedRawData SET [RowLog Len] = CONVERT(VARBINARY(8000), REVERSE(CAST('' AS XML).value('xs:hexBinary(substring(sql:column("[RowLog Length]"),0))', 'varbinary(Max)'))) FROM @ModifiedRawData WHERE [LINK ID] = 0 UPDATE @ModifiedRawData SET [RowLog Contents 0] = CAST('' AS XML).value('xs:hexBinary(subs