• 【sqlserver】在没有数据库备份的情况下,获得操作记录信息【code】


    原文链接:How to recover modified records from SQL Server without Backup

    SQL Server 2005或以上恢复修改的数据。(注:这个脚本可以恢复以下数据类型与CS整理兼容)。

    • tinyint
    • smallint
    • int
    • bigint
    • bit
    • char
    • varchar
    • nchar
    • nvarchar
    • datetime
    • smalldatetime
    • money
    • smallmoney
    • decimal
    • numeric
    • real
    • float
    • binary
    • varbinary
    • uniqueidentifier

    让我解释这个问题演示简单的例子。

    --Create Table
    CREATE TABLE [dbo].[Student](  
          [Sno] [int] NOT NULL,  
          [Student ID] nvarchar(6) Not NULL ,  
          [Student name] [varchar](50) NOT NULL,  
          [Date of Birth]  datetime not null,  
          [Weight] [int] NULL)  
    --Inserting data into table
    Insert into dbo.[Student] values (1,'STD001','Bob','2003-12-31',40)  
    Insert into dbo.[Student] values (2,'STD002','Alexander','2004-11-15',35) 
    --Check the existence of the data
    Select * from dbo.[Student]
     
    Update [Student] Set [Student Name]='Bob jerry' --Where [SNO]=1 forget to use where clause
     
    Select * from dbo.[Student]
     
    存储过程代码:
      1 Create PROCEDURE Recover_Modified_Data_Proc
      2 @Database_Name NVARCHAR(MAX),
      3 @SchemaName_n_TableName NVARCHAR(MAX),
      4 @Date_From datetime='1900/01/01',
      5 @Date_To datetime ='9999/12/31'
      6 AS
      7 DECLARE @parms nvarchar(1024)
      8 DECLARE @Fileid INT
      9 DECLARE @Pageid INT
     10 DECLARE @Slotid INT
     11 DECLARE @RowLogContents0 VARBINARY(8000)
     12 DECLARE @RowLogContents1 VARBINARY(8000)
     13 DECLARE @RowLogContents3 VARBINARY(8000)
     14 DECLARE @RowLogContents3_Var VARCHAR(MAX)
     15  
     16 DECLARE @RowLogContents4 VARBINARY(8000)
     17 DECLARE @LogRecord VARBINARY(8000)
     18 DECLARE @LogRecord_Var VARCHAR(MAX)
     19  
     20 DECLARE @ConsolidatedPageID VARCHAR(MAX)
     21 Declare @AllocUnitID as bigint
     22 Declare @TransactionID as VARCHAR(MAX)
     23 Declare @Operation as VARCHAR(MAX)
     24 Declare @DatabaseCollation VARCHAR(MAX)
     25  
     26  
     27 /*  Pick The actual data
     28 */
     29 declare @temppagedata table
     30 (
     31 [ParentObject] sysname,
     32 [Object] sysname,
     33 [Field] sysname,
     34 [Value] sysname)
     35  
     36 declare @pagedata table
     37 (
     38 [Page ID] sysname,
     39 [AllocUnitId] bigint,
     40 [ParentObject] sysname,
     41 [Object] sysname,
     42 [Field] sysname,
     43 [Value] sysname)
     44  
     45  
     46     DECLARE Page_Data_Cursor CURSOR FOR
     47     /*We need to filter LOP_MODIFY_ROW,LOP_MODIFY_COLUMNS from log for modified records & Get its Slot No, Page ID & AllocUnit ID*/
     48     SELECT [PAGE ID],[Slot ID],[AllocUnitId]
     49     FROM    sys.fn_dblog(NULL, NULL)  
     50     WHERE   
     51     AllocUnitId IN
     52     (Select [Allocation_unit_id] from sys.allocation_units allocunits
     53     INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3)  
     54     AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 
     55     AND partitions.partition_id = allocunits.container_id)  
     56     Where object_id=object_ID('' + @SchemaName_n_TableName + ''))
     57     AND Operation IN ('LOP_MODIFY_ROW','LOP_MODIFY_COLUMNS')  AND [Context] IN ('LCX_HEAP','LCX_CLUSTERED')
     58     /*Use this subquery to filter the date*/
     59  
     60     AND [TRANSACTION ID] IN (SELECT DISTINCT [TRANSACTION ID] FROM    sys.fn_dblog(NULL, NULL) 
     61     WHERE Context IN ('LCX_NULL') AND Operation in ('LOP_BEGIN_XACT')  
     62     AND [Transaction Name]='UPDATE'
     63     AND  CONVERT(NVARCHAR(11),[Begin Time]) BETWEEN @Date_From AND @Date_To)
     64  
     65     /****************************************/
     66  
     67     GROUP BY [PAGE ID],[Slot ID],[AllocUnitId]
     68     ORDER BY [Slot ID]    
     69   
     70     OPEN Page_Data_Cursor
     71  
     72     FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,@AllocUnitID
     73  
     74     WHILE @@FETCH_STATUS = 0
     75     BEGIN
     76         DECLARE @hex_pageid AS VARCHAR(Max)
     77         /*Page ID contains File Number and page number It looks like 0001:00000130.
     78           In this example 0001 is file Number &  00000130 is Page Number & These numbers are in Hex format*/
     79         SET @Fileid=SUBSTRING(@ConsolidatedPageID,0,CHARINDEX(':',@ConsolidatedPageID)) -- Seperate File ID from Page ID
     80         SET @hex_pageid ='0x'+ SUBSTRING(@ConsolidatedPageID,CHARINDEX(':',@ConsolidatedPageID)+1,Len(@ConsolidatedPageID))  ---Seperate the page ID
     81         
     82         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
     83         FROM (SELECT CASE substring(@hex_pageid, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos) 
     84                      
     85         DELETE @temppagedata
     86         -- Now we need to get the actual data (After modification) from the page
     87         INSERT INTO @temppagedata EXEC( 'DBCC PAGE(' + @DataBase_Name + ', ' + @fileid + ', ' + @pageid + ', 3) with tableresults,no_infomsgs;'); 
     88         -- Add Page Number and allocUnit ID in data to identity which one page it belongs to.                    
     89         INSERT INTO @pagedata SELECT @ConsolidatedPageID,@AllocUnitID,[ParentObject],[Object],[Field] ,[Value] FROM @temppagedata
     90  
     91         FETCH NEXT FROM Page_Data_Cursor INTO  @ConsolidatedPageID, @Slotid,@AllocUnitID
     92     END
     93  
     94 CLOSE Page_Data_Cursor
     95 DEALLOCATE Page_Data_Cursor
     96  
     97 DECLARE @Newhexstring VARCHAR(MAX);
     98  
     99  
    100 DECLARE @ModifiedRawData TABLE
    101 (
    102   [ID] INT IDENTITY(1,1),
    103   [PAGE ID] VARCHAR(MAX),
    104   [Slot ID] INT,
    105   [AllocUnitId] BIGINT,
    106   [RowLog Contents 0_var] VARCHAR(MAX),
    107   [RowLog Contents 0] VARBINARY(8000)
    108 )
    109 --The modified data is in multiple rows in the page, so we need to convert it into one row as a single hex value.
    110 --This hex value is in string format
    111 INSERT INTO @ModifiedRawData ([PAGE ID],[Slot ID],[AllocUnitId]
    112 ,[RowLog Contents 0_var])
    113 SELECT B.[PAGE ID],A.[Slot ID],A.[AllocUnitId]
    114 ,(
    115 SELECT REPLACE(STUFF((SELECT REPLACE(SUBSTRING([VALUE],CHARINDEX(':',[Value])+1,48),'','')
    116 FROM @pagedata C  WHERE B.[Page ID]= C.[Page ID] And A.[Slot ID] =LTRIM(RTRIM(SUBSTRING(C.[ParentObject],5,3))) And [Object] Like '%Memory Dump%'
    117 Group By [Value] FOR XML PATH('') ),1,1,'') ,' ','')
    118 ) AS [Value]
    119  
    120 FROM sys.fn_dblog(NULL, NULL) A
    121 INNER JOIN @pagedata B On A.[PAGE ID]=B.[PAGE ID]
    122 AND A.[AllocUnitId]=B.[AllocUnitId]
    123 AND A.[Slot ID] =LTRIM(RTRIM(Substring(B.[ParentObject],5,3)))
    124 AND B.[Object] Like '%Memory Dump%'
    125 WHERE A.AllocUnitId IN
    126 (Select [Allocation_unit_id] from sys.allocation_units allocunits
    127 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3)
    128 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 
    129 AND partitions.partition_id = allocunits.container_id)  
    130 Where object_id=object_ID('' + @SchemaName_n_TableName + ''))
    131 AND Operation in ('LOP_MODIFY_ROW','LOP_MODIFY_COLUMNS') AND [Context] IN ('LCX_HEAP','LCX_CLUSTERED')
    132 /*Use this subquery to filter the date*/
    133  
    134 AND [TRANSACTION ID] IN (Select DISTINCT [TRANSACTION ID] FROM sys.fn_dblog(NULL, NULL) 
    135 Where Context IN ('LCX_NULL') AND Operation IN ('LOP_BEGIN_XACT')  
    136 AND [Transaction Name]='UPDATE'
    137 AND  CONVERT(NVARCHAR(11),[Begin Time]) BETWEEN @Date_From AND @Date_To)
    138  
    139 /****************************************/
    140 GROUP BY B.[PAGE ID],A.[Slot ID],A.[AllocUnitId]--,[Transaction ID]
    141 ORDER BY [Slot ID]
    142  
    143  
    144 -- Convert the hex value data in string, convert it into Hex value as well. 
    145 UPDATE @ModifiedRawData  SET [RowLog Contents 0] = cast('' AS XML).value('xs:hexBinary(substring(sql:column("[RowLog Contents 0_var]"), 0) )', 'varbinary(max)')
    146 FROM @ModifiedRawData
    147  
    148 ---Now we have modifed data plus its slot ID , page ID and allocunit as well.
    149 --After that we need to get the old values before modfication, these datas are in chunks.
    150 DECLARE Page_Data_Cursor CURSOR FOR
    151  
    152 Select [PAGE ID],[Slot ID],[AllocUnitId],[Transaction ID],[RowLog Contents 0], [RowLog Contents 1],[RowLog Contents 3],[RowLog Contents 4]
    153 ,Substring ([Log Record],[Log Record Fixed Length],([Log Record Length]+1)-([Log Record Fixed Length])) as [Log Record]
    154 ,Operation
    155 FROM    sys.fn_dblog(NULL, NULL)  
    156 WHERE   AllocUnitId IN
    157 (Select [Allocation_unit_id] from sys.allocation_units allocunits
    158 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3)  
    159 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 
    160 AND partitions.partition_id = allocunits.container_id)  
    161 Where object_id=object_ID('' + @SchemaName_n_TableName + ''))
    162 AND Operation in ('LOP_MODIFY_ROW','LOP_MODIFY_COLUMNS') And [Context] IN ('LCX_HEAP','LCX_CLUSTERED')
    163 /*Use this sub query to filter the date*/
    164  
    165 AND [TRANSACTION ID] IN (Select DISTINCT [TRANSACTION ID] FROM    sys.fn_dblog(NULL, NULL) 
    166 WHERE Context IN ('LCX_NULL') AND Operation IN ('LOP_BEGIN_XACT')  
    167 AND [Transaction Name]='UPDATE'
    168 AND  CONVERT(NVARCHAR(11),[Begin Time]) BETWEEN @Date_From AND @Date_To)
    169  
    170 /****************************************/
    171 Order By [Slot ID],[Transaction ID] DESC
    172  
    173 OPEN Page_Data_Cursor
    174  
    175     FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,@AllocUnitID,@TransactionID,@RowLogContents0,@RowLogContents1,@RowLogContents3,@RowLogContents4,@LogRecord,@Operation
    176     WHILE @@FETCH_STATUS = 0
    177     BEGIN
    178            IF @Operation ='LOP_MODIFY_ROW' 
    179               BEGIN
    180                       /* If it is @Operation Type is 'LOP_MODIFY_ROW' then it is very simple to recover the modified data. The old data is in [RowLog Contents 0] Field and modified data is in [RowLog Contents 1] Field. Simply replace it with the modified data and get the old data.
    181                       */
    182                       INSERT INTO @ModifiedRawData ([PAGE ID],[Slot ID],[AllocUnitId],[RowLog Contents 0_var]) 
    183                       SELECT TOP 1  @ConsolidatedPageID AS [PAGE ID],@Slotid AS [Slot ID],@AllocUnitID AS [AllocUnitId]
    184                       ,REPLACE (UPPER([RowLog Contents 0_var]),UPPER(CAST('' AS XML).value('xs:hexBinary(sql:variable("@RowLogContents1") )', 'varchar(max)')),UPPER(cast('' AS XML).value('xs:hexBinary(sql:variable("@RowLogContents0") )', 'varchar(max)'))) AS [RowLog Contents 0_var]
    185                       FROM  @ModifiedRawData WHERE [PAGE ID]=@ConsolidatedPageID And [Slot ID]=@Slotid And [AllocUnitId]=@AllocUnitID ORDER BY [ID] DESC
    186  
    187                       --- Convert the old data which is in string format to hex format.
    188                       UPDATE @ModifiedRawData  SET [RowLog Contents 0] = cast('' AS XML).value('xs:hexBinary(substring(sql:column("[RowLog Contents 0_var]"), 0) )', 'varbinary(max)')
    189                       FROM @ModifiedRawData Where [Slot ID]=@SlotID
    190  
    191               END
    192          IF @Operation ='LOP_MODIFY_COLUMNS'                   
    193              BEGIN
    194  
    195                           /* If it is @Operation Type is 'LOP_MODIFY_ROW' then we need to follow a different procedure to recover modified
    196                              .Because this time the data is also in chunks but merge with the data log.
    197                           */
    198                             --First, we need to get the [RowLog Contents 3] Because in [Log Record] field the modified data is available after the [RowLog Contents 3] data.
    199                             SET @RowLogContents3_Var=cast('' AS XML).value('xs:hexBinary(sql:variable("@RowLogContents3") )', 'varchar(max)')
    200                             SET @LogRecord_Var =cast('' AS XML).value('xs:hexBinary(sql:variable("@LogRecord"))', 'varchar(max)')
    201             
    202                             DECLARE @RowLogData_Var VARCHAR(Max)
    203                             DECLARE @RowLogData_Hex VARBINARY(Max)
    204                             ---First get the modifed data chunks in string format 
    205                             SET @RowLogData_Var = SUBSTRING(@LogRecord_Var, CHARINDEX(@RowLogContents3_Var,@LogRecord_Var) +LEN(@RowLogContents3_Var) ,LEN(@LogRecord_Var))
    206                             --Then convert it into the hex values.
    207                             SELECT @RowLogData_Hex=CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@RowLogData_Var"),0) )', 'varbinary(max)')
    208                             FROM (SELECT CASE SUBSTRING(@RowLogData_Var, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
    209                             DECLARE @TotalFixedLengthData INT 
    210                             DECLARE @FixedLength_Offset INT
    211                             DECLARE @VariableLength_Offset INT
    212                             DECLARE @VariableLength_Offset_Start INT
    213                             DECLARE @VariableLengthIncrease INT
    214                             DECLARE @FixedLengthIncrease INT
    215                             DECLARE @OldFixedLengthStartPosition INT
    216                             DECLARE @FixedLength_Loc INT
    217                             DECLARE @VariableLength_Loc INT
    218                             DECLARE @FixedOldValues VARBINARY(MAX)
    219                             DECLARE @FixedNewValues VARBINARY(MAX)
    220                             DECLARE @VariableOldValues VARBINARY(MAX)
    221                             DECLARE @VariableNewValues VARBINARY(MAX)
    222                          
    223                             -- Before recovering the modfied data we need to get the total fixed length data size and start position of the varaible data
    224                              
    225                             SELECT TOP 1 @TotalFixedLengthData=CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0] , 2 + 1, 2)))) 
    226                             ,@VariableLength_Offset_Start=CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0] , 2 + 1, 2))))+5+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))
    227                             FROM @ModifiedRawData
    228                             ORDER BY [ID] DESC
    229  
    230                             SET @FixedLength_Offset= CONVERT(BINARY(2),REVERSE(CONVERT(BINARY(4),(@RowLogContents0))))--)
    231                             SET @VariableLength_Offset=CONVERT(int,CONVERT(BINARY(2),REVERSE(@RowLogContents0)))
    232                              
    233                             /* We already have modified data chunks in @RowLogData_Hex but this data is in merge format (modified plus actual data)
    234                               So , here we need [Row Log Contents 1] field , because in this field we have the data length both the modified and actual data
    235                                so this length will help us to break it into original and modified data chunks.
    236                             */
    237                             SET @FixedLength_Loc= CONVERT(INT,SUBSTRING(@RowLogContents1,1,1))
    238                             SET @VariableLength_Loc =CONVERT(INT,SUBSTRING(@RowLogContents1,3,1))
    239  
    240                             /*First , we need to break Fix length data actual with the help of data length  */
    241                             SET @OldFixedLengthStartPosition= CHARINDEX(@RowLogContents4,@RowLogData_Hex)
    242                             SET @FixedOldValues = SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition,@FixedLength_Loc)
    243                             SET @FixedLengthIncrease = (CASE WHEN (Len(@FixedOldValues)%4)=0 THEN 1 ELSE (4-(LEN(@FixedOldValues)%4))  END)
    244                             /*After that , we need to break Fix length data modified data with the help of data length  */
    245                             SET @FixedNewValues =SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition+@FixedLength_Loc+@FixedLengthIncrease,@FixedLength_Loc) 
    246  
    247                             /*Same we need to break the variable data with the help of data length*/
    248                             SET @VariableOldValues =SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition+@FixedLength_Loc+@FixedLengthIncrease+@FixedLength_Loc+(@FixedLengthIncrease),@VariableLength_Loc)
    249                             SET @VariableLengthIncrease =  (CASE WHEN (LEN(@VariableOldValues)%4)=0 THEN 1 ELSE (4-(Len(@VariableOldValues)%4))+1  END) 
    250                             SET @VariableOldValues =(Case When @VariableLength_Loc =1 Then  @VariableOldValues+0x00 else @VariableOldValues end)
    251                  
    252                             SET @VariableNewValues =SUBSTRING(SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition+@FixedLength_Loc+@FixedLengthIncrease+@FixedLength_Loc+(@FixedLengthIncrease-1)+@VariableLength_Loc+@VariableLengthIncrease,Len(@RowLogData_Hex)+1),1,Len(@RowLogData_Hex)+1) --LEN(@VariableOldValues)
    253                             
    254                             /*here we need to replace the fixed length &  variable length actaul data with modifed data 
    255                             */
    256                              
    257                             Select top 1 @VariableNewValues=Case
    258                             When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)+1),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)+1)
    259                             When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)),[RowLog Contents 0])<>0 Then  Substring(@VariableNewValues,0,Len(@VariableNewValues))
    260                             When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)-1),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)-1)--3 --Substring(@VariableNewValues,0,Len(@VariableNewValues)-1)
    261                             When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)-2),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)-2)
    262                             When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)-3),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)-3) --5--Substring(@VariableNewValues,0,Len(@VariableNewValues)-3)
    263                             End
    264                             FROM @ModifiedRawData  Where [Slot ID]=@SlotID  ORDER BY [ID] DESC
    265                                           
    266                             INSERT INTO @ModifiedRawData ([PAGE ID],[Slot ID],[AllocUnitId],[RowLog Contents 0_var],[RowLog Contents 0]) 
    267                             SELECT TOP 1  @ConsolidatedPageID AS [PAGE ID],@Slotid AS [Slot ID],@AllocUnitID AS [AllocUnitId],NULL
    268                             ,CAST(REPLACE(SUBSTRING([RowLog Contents 0],0,@TotalFixedLengthData+1),@FixedNewValues, @FixedOldValues) AS VARBINARY(max))
    269                             + SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 1, 2)
    270                             + SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 3, CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 1, 2))))/8.0)))
    271                             + SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 3 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 1, 2))))/8.0)), 2)
    272                             + Substring([RowLog Contents 0],@VariableLength_Offset_Start,(@VariableLength_Offset-(@VariableLength_Offset_Start-1)))
    273                             + CAST(REPLACE(SUBSTRING([RowLog Contents 0],@VariableLength_Offset+1,Len(@VariableNewValues))
    274                             , @VariableNewValues
    275                             , @VariableOldValues) AS VARBINARY) 
    276                             + Substring([RowLog Contents 0],@VariableLength_Offset+Len(@VariableNewValues)+1,LEN([RowLog Contents 0]))
    277                             FROM @ModifiedRawData  Where [Slot ID]=@SlotID  ORDER BY [ID] DESC
    278                             
    279              END
    280  
    281         FETCH NEXT FROM Page_Data_Cursor INTO   @ConsolidatedPageID, @Slotid,@AllocUnitID,@TransactionID,@RowLogContents0,@RowLogContents1,@RowLogContents3,@RowLogContents4,@LogRecord,@Operation
    282     END
    283  
    284 CLOSE Page_Data_Cursor
    285 DEALLOCATE Page_Data_Cursor
    286  
    287 DECLARE @RowLogContents VARBINARY(8000)
    288 Declare @AllocUnitName NVARCHAR(Max)
    289 Declare @SQL NVARCHAR(Max)
    290  
    291 DECLARE @bitTable TABLE
    292 (
    293   [ID] INT,
    294   [Bitvalue] INT
    295 )
    296 ----Create table to set the bit position of one byte.
    297  
    298 INSERT INTO @bitTable
    299 SELECT 0,2 UNION ALL
    300 SELECT 1,2 UNION ALL
    301 SELECT 2,4 UNION ALL
    302 SELECT 3,8 UNION ALL
    303 SELECT 4,16 UNION ALL
    304 SELECT 5,32 UNION ALL
    305 SELECT 6,64 UNION ALL
    306 SELECT 7,128
    307  
    308 --Create table to collect the row data.
    309 DECLARE @DeletedRecords TABLE
    310 (
    311     [ID] INT IDENTITY(1,1),
    312     [RowLogContents]    VARBINARY(8000),
    313     [AllocUnitID]       BIGINT,
    314     [Transaction ID]    NVARCHAR(Max),
    315     [Slot ID]           INT,
    316     [FixedLengthData]   SMALLINT,
    317     [TotalNoOfCols]     SMALLINT,
    318     [NullBitMapLength]  SMALLINT,
    319     [NullBytes]         VARBINARY(8000),
    320     [TotalNoofVarCols]  SMALLINT,
    321     [ColumnOffsetArray] VARBINARY(8000),
    322     [VarColumnStart]    SMALLINT,
    323     [NullBitMap]        VARCHAR(MAX)
    324 )
    325 --Create a common table expression to get all the row data plus how many bytes we have for each row.
    326 ;WITH RowData AS (
    327 SELECT
    328  
    329 [RowLog Contents 0] AS [RowLogContents] 
    330  
    331 ,@AllocUnitID AS [AllocUnitID] 
    332  
    333 ,[ID] AS [Transaction ID]  
    334  
    335 ,[Slot ID] as [Slot ID]
    336 --[Fixed Length Data] = Substring (RowLog content 0, Status Bit A+ Status Bit B + 1,2 bytes)
    337 ,CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) AS [FixedLengthData]  --@FixedLengthData
    338  
    339  --[TotalnoOfCols] =  Substring (RowLog content 0, [Fixed Length Data] + 1,2 bytes)
    340 ,CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    341 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) as  [TotalNoOfCols]
    342  
    343 --[NullBitMapLength]=ceiling([Total No of Columns] /8.0)
    344 ,CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    345 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)) as [NullBitMapLength] 
    346  
    347 --[Null Bytes] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [NullBitMapLength] )
    348 ,SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3,
    349 CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    350 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0))) as [NullBytes]
    351  
    352 --[TotalNoofVarCols] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 )
    353 ,(CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN
    354 CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
    355 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3
    356 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    357 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)), 2))))  ELSE null  END) AS [TotalNoofVarCols] 
    358  
    359 --[ColumnOffsetArray]= Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 , [TotalNoofVarCols]*2 )
    360 ,(CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN
    361 SUBSTRING([RowLog Contents 0]
    362 , CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3
    363 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    364 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)) + 2
    365 , (CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN
    366 CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
    367 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3
    368 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    369 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)), 2))))  ELSE null  END)
    370 * 2)  ELSE null  END) AS [ColumnOffsetArray] 
    371  
    372 --  Variable column Start = Status Bit A+ Status Bit B + [Fixed Length Data] + [Null Bitmap length] + 2+([TotalNoofVarCols]*2)
    373 ,CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1)In (0x30,0x70)
    374 THEN  (
    375 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 4 
    376  
    377 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    378 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)) 
    379  
    380 + ((CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN
    381 CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
    382 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3
    383 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2)
    384 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)), 2))))  ELSE null  END) * 2)) 
    385  
    386 ELSE null End AS [VarColumnStart]
    387 From @ModifiedRawData
    388  
    389 ),
    390  
    391 ---Use this technique to repeate the row till the no of bytes of the row.
    392 N1 (n) AS (SELECT 1 UNION ALL SELECT 1),
    393 N2 (n) AS (SELECT 1 FROM N1 AS X, N1 AS Y),
    394 N3 (n) AS (SELECT 1 FROM N2 AS X, N2 AS Y),
    395 N4 (n) AS (SELECT ROW_NUMBER() OVER(ORDER BY X.n)
    396            FROM N3 AS X, N3 AS Y)
    397  
    398 insert into @DeletedRecords
    399 Select   RowLogContents
    400         ,[AllocUnitID]
    401         ,[Transaction ID]
    402         ,[Slot ID]
    403         ,[FixedLengthData]
    404         ,[TotalNoOfCols]
    405         ,[NullBitMapLength]
    406         ,[NullBytes]
    407         ,[TotalNoofVarCols]
    408         ,[ColumnOffsetArray]
    409         ,[VarColumnStart]
    410          --Get the Null value against each column (1 means null zero means not null)
    411         ,[NullBitMap]=(REPLACE(STUFF((SELECT ',' +
    412         (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]
    413 FROM
    414 N4 AS Nums
    415 Join RowData AS C ON n<=NullBitMapLength
    416 Cross Join @bitTable WHERE C.[RowLogContents]=D.[RowLogContents] ORDER BY [RowLogContents],n ASC FOR XML PATH('')),1,1,''),',',''))
    417 FROM RowData D
    418  
    419 CREATE TABLE [#temp_Data]
    420 (
    421    
    422     [FieldName]  VARCHAR(MAX) COLLATE database_default NOT NULL,
    423     [FieldValue] VARCHAR(MAX) COLLATE database_default NULL,
    424     [Rowlogcontents] VARBINARY(8000),
    425     [Transaction ID] VARCHAR(MAX) COLLATE database_default NOT NULL,
    426     [Slot ID] INT,
    427     [NonID] INT,
    428     --[System_type_id] int
    429  
    430 )
    431 ---Create common table expression and join it with the rowdata table
    432 --to get each column details
    433 ;With CTE AS (
    434 /*This part is for variable data columns*/
    435 SELECT
    436 A.[ID],
    437 Rowlogcontents,
    438 [Transaction ID],
    439 [Slot ID],
    440 NAME ,
    441 cols.leaf_null_bit AS nullbit,
    442 leaf_offset,
    443 ISNULL(syscolumns.length, cols.max_length) AS [length],
    444 cols.system_type_id,
    445 cols.leaf_bit_position AS bitpos,
    446 ISNULL(syscolumns.xprec, cols.precision) AS xprec,
    447 ISNULL(syscolumns.xscale, cols.scale) AS xscale,
    448 SUBSTRING([nullBitMap], cols.leaf_null_bit, 1) AS is_null,
    449 --Calculate the variable column size from the variable column offset array
    450 (CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN
    451 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * leaf_offset*-1) - 1, 2)))) ELSE 0 END) AS [Column value Size],
    452  
    453 ---Calculate the column length
    454 (CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN  CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * (leaf_offset*-1)) - 1, 2))))
    455 - ISNULL(NULLIF(CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * ((leaf_offset*-1) - 1)) - 1, 2)))), 0), [varColumnStart])
    456 ELSE 0 END) AS [Column Length]
    457  
    458 --Get the Hexa decimal value from the RowlogContent
    459 --HexValue of the variable column=Substring([Column value Size] - [Column Length] + 1,[Column Length])
    460 --This is the data of your column but in the Hexvalue
    461 ,CASE WHEN SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=1 THEN NULL ELSE
    462 SUBSTRING(Rowlogcontents,((CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * leaf_offset*-1) - 1, 2)))) ELSE 0 END)
    463 - ((CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN  CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * (leaf_offset*-1)) - 1, 2))))
    464 - ISNULL(NULLIF(CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * ((leaf_offset*-1) - 1)) - 1, 2)))), 0), [varColumnStart])
    465 ELSE 0 END))) + 1,((CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN  CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * (leaf_offset*-1)) - 1, 2))))
    466 - ISNULL(NULLIF(CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * ((leaf_offset*-1) - 1)) - 1, 2)))), 0), [varColumnStart])
    467 ELSE 0 END))) END AS hex_Value
    468  
    469 FROM @DeletedRecords A
    470 Inner Join sys.allocation_units allocunits On A.[AllocUnitId]=allocunits.[Allocation_Unit_Id]
    471 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3)
    472 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 AND partitions.partition_id = allocunits.container_id)
    473 INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id
    474 LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id AND syscolumns.colid = cols.partition_column_id
    475 WHERE leaf_offset<0
    476  
    477 UNION
    478 /*This part is for fixed data columns*/
    479 SELECT 
    480 A.[ID],
    481 Rowlogcontents,
    482 [Transaction ID],
    483 [Slot ID],
    484 NAME ,
    485 cols.leaf_null_bit AS nullbit,
    486 leaf_offset,
    487 ISNULL(syscolumns.length, cols.max_length) AS [length],
    488 cols.system_type_id,
    489 cols.leaf_bit_position AS bitpos,
    490 ISNULL(syscolumns.xprec, cols.precision) AS xprec,
    491 ISNULL(syscolumns.xscale, cols.scale) AS xscale,
    492 SUBSTRING([nullBitMap], cols.leaf_null_bit, 1) AS is_null,
    493 (SELECT TOP 1 ISNULL(SUM(CASE WHEN C.leaf_offset >1 THEN max_length ELSE 0 END),0) FROM
    494 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],
    495 syscolumns.length AS [Column Length]
    496  
    497 ,CASE WHEN SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=1 THEN NULL ELSE
    498 SUBSTRING
    499 (
    500 Rowlogcontents,(SELECT TOP 1 ISNULL(SUM(CASE WHEN C.leaf_offset >1 THEN max_length ELSE 0 END),0) FROM
    501 sys.system_internals_partition_columns C where cols.partition_id =C.partition_id And C.leaf_null_bit<cols.leaf_null_bit)+5
    502 ,syscolumns.length) END AS hex_Value
    503 FROM @DeletedRecords A
    504 Inner Join sys.allocation_units allocunits ON A.[AllocUnitId]=allocunits.[Allocation_Unit_Id]
    505 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3)
    506  AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 AND partitions.partition_id = allocunits.container_id)
    507 INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id
    508 LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id AND syscolumns.colid = cols.partition_column_id
    509 WHERE leaf_offset>0 )
    510  
    511 --Converting data from Hexvalue to its orgional datatype.
    512 --Implemented datatype conversion mechanism for each datatype
    513 --Select * from sys.columns Where [object_id]=object_id('' + @SchemaName_n_TableName + '')
    514 --Select * from CTE
    515  
    516 INSERT INTO #temp_Data
    517 SELECT
    518 NAME,
    519 CASE
    520  WHEN system_type_id IN (231, 239) THEN  LTRIM(RTRIM(CONVERT(NVARCHAR(max),hex_Value)))  --NVARCHAR ,NCHAR
    521  WHEN system_type_id IN (167,175) THEN  LTRIM(RTRIM(CONVERT(VARCHAR(max),REPLACE(hex_Value, 0x00, 0x20))))  --VARCHAR,CHAR
    522  WHEN system_type_id = 48 THEN CONVERT(VARCHAR(MAX), CONVERT(TINYINT, CONVERT(BINARY(1), REVERSE (hex_Value)))) --TINY INTEGER
    523  WHEN system_type_id = 52 THEN CONVERT(VARCHAR(MAX), CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (hex_Value)))) --SMALL INTEGER
    524  WHEN system_type_id = 56 THEN CONVERT(VARCHAR(MAX), CONVERT(INT, CONVERT(BINARY(4), REVERSE(hex_Value)))) -- INTEGER
    525  WHEN system_type_id = 127 THEN CONVERT(VARCHAR(MAX), CONVERT(BIGINT, CONVERT(BINARY(8), REVERSE(hex_Value))))-- BIG INTEGER
    526  WHEN system_type_id = 61 Then CONVERT(VARCHAR(MAX),CONVERT(DATETIME,CONVERT(VARBINARY(8000),REVERSE (hex_Value))),100) --DATETIME
    527  --WHEN system_type_id IN( 40) Then CONVERT(VARCHAR(MAX),CONVERT(DATE,CONVERT(VARBINARY(8000),(hex_Value))),100) --DATE This datatype only works for SQL Server 2008
    528  WHEN system_type_id =58 Then CONVERT(VARCHAR(MAX),CONVERT(SMALLDATETIME,CONVERT(VARBINARY(8000),REVERSE(hex_Value))),100) --SMALL DATETIME
    529  WHEN system_type_id = 108 THEN CONVERT(VARCHAR(MAX), CAST(CONVERT(NUMERIC(38,30), CONVERT(VARBINARY,CONVERT(VARBINARY,xprec)+CONVERT(VARBINARY,xscale))+CONVERT(VARBINARY(1),0) + hex_Value) as FLOAT)) --- NUMERIC
    530  WHEN system_type_id In(60,122) THEN CONVERT(VARCHAR(MAX),Convert(MONEY,Convert(VARBINARY(8000),Reverse(hex_Value))),2) --MONEY,SMALLMONEY
    531  WHEN system_type_id =106 THEN CONVERT(VARCHAR(MAX), CAST(CONVERT(Decimal(38,34), CONVERT(VARBINARY,Convert(VARBINARY,xprec)+CONVERT(VARBINARY,xscale))+CONVERT(VARBINARY(1),0) + hex_Value) as FLOAT)) --- DECIMAL
    532  WHEN system_type_id = 104 THEN CONVERT(VARCHAR(MAX),CONVERT (BIT,CONVERT(BINARY(1), hex_Value)%2))  -- BIT
    533  WHEN system_type_id =62 THEN  RTRIM(LTRIM(STR(CONVERT(FLOAT,SIGN(CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT)) * (1.0 + (CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT) & 0x000FFFFFFFFFFFFF) * POWER(CAST(2 AS FLOAT), -52)) * POWER(CAST(2 AS FLOAT),((CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT) & 0x7ff0000000000000) / EXP(52 * LOG(2))-1023))),53,LEN(hex_Value)))) --- FLOAT
    534  When system_type_id =59 THEN  Left(LTRIM(STR(CAST(SIGN(CAST(Convert(VARBINARY(8000),REVERSE(hex_Value)) AS BIGINT))* (1.0 + (CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT) & 0x007FFFFF) * POWER(CAST(2 AS Real), -23)) * POWER(CAST(2 AS Real),(((CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS INT) )& 0x7f800000)/ EXP(23 * LOG(2))-127))AS REAL),23,23)),8) --Real
    535  WHEN system_type_id In (165,173) THEN (CASE WHEN CHARINDEX(0x,cast('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))', 'VARBINARY(8000)')) = 0 THEN '0x' ELSE '' END) +cast('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))', 'varchar(max)') -- BINARY,VARBINARY
    536  WHEN system_type_id =36 THEN CONVERT(VARCHAR(MAX),CONVERT(UNIQUEIDENTIFIER,hex_Value)) --UNIQUEIDENTIFIER
    537  END AS FieldValue
    538 ,[Rowlogcontents]
    539 ,[Transaction ID]
    540 ,[Slot ID]
    541 ,[ID]
    542 FROM CTE ORDER BY nullbit
    543  
    544 /*Create Update statement*/
    545 /*Now we have the modified and actual data as well*/
    546 /*We need to create the update statement in case of recovery*/
    547  
    548 ;With CTE AS (SELECT
    549 (CASE
    550 WHEN system_type_id In (167,175,189) THEN QUOTENAME([Name]) + '=' + ISNULL(+ '''' + [A].[FieldValue]+ '''','NULL')+ ' ,'+' '
    551 WHEN system_type_id In (231,239) THEN  QUOTENAME([Name]) + '='  + ISNULL(+ 'N''' +[A].[FieldValue]+ '''','NULL')+ ' ,'+''
    552 WHEN system_type_id In (58,40,61,36) THEN QUOTENAME([Name]) + '='  + ISNULL(+  ''''+[A].[FieldValue]+ '''','NULL') + '  ,'+' '
    553 WHEN system_type_id In (48,52,56,59,60,62,104,106,108,122,127) THEN QUOTENAME([Name]) + '='  + ISNULL([A].[FieldValue],'NULL')+ ' ,'+' '
    554 END) as [Field]
    555 ,A.[Slot ID]
    556 ,A.[Transaction ID] as [Transaction ID]
    557 ,'D' AS [Type] 
    558 ,[A].Rowlogcontents
    559 ,[A].[NonID]
    560 FROM #temp_Data AS [A]
    561 INNER JOIN #temp_Data AS [B] ON [A].[FieldName]=[B].[FieldName]
    562 AND [A].[Slot ID]=[B].[Slot ID]
    563 --And [A].[Transaction ID]=[B].[Transaction ID]+1
    564 AND [B].[Transaction ID]=  (SELECT Min(Cast([Transaction ID] as int)) as [Transaction ID]  FROM #temp_Data AS [C]
    565 WHERE [A].[Slot ID]=[C].[Slot ID]
    566 GROUP BY [Slot ID])
    567 INNER JOIN sys.columns [D] On  [object_id]=object_id('' + @SchemaName_n_TableName + '')
    568 AND A.[Fieldname] = D.[name]
    569 WHERE ISNULL([A].[FieldValue],'')<>ISNULL([B].[FieldValue],'')
    570 UNION ALL
    571  
    572 SELECT(CASE
    573 WHEN system_type_id In (167,175,189) THEN QUOTENAME([Name]) + '=' + ISNULL(+ '''' + [A].[FieldValue]+ '''','NULL')+ ' AND '+''
    574 WHEN system_type_id In (231,239) THEN  QUOTENAME([Name]) + '='+ ISNULL(+ 'N''' +[A].[FieldValue]+ '''','NULL')+ ' AND '+''
    575 WHEN system_type_id In (58,40,61,36) THEN QUOTENAME([Name]) + '=' + ISNULL(+  ''''+[A].[FieldValue]+ '''','NULL') + ' AND '+''
    576 WHEN system_type_id In (48,52,56,59,60,62,104,106,108,122,127) THEN QUOTENAME([Name]) + '='  + ISNULL([A].[FieldValue],'NULL') + ' AND '+''
    577 END) AS [Field]
    578 ,A.[Slot ID]
    579 ,A.[Transaction ID] AS [Transaction ID]
    580 ,'S' AS [Type]
    581 ,[A].Rowlogcontents
    582 ,[A].[NonID]
    583 FROM #temp_Data AS [A]
    584 INNER JOIN #temp_Data AS [B] ON [A].[FieldName]=[B].[FieldName]
    585 AND [A].[Slot ID]=[B].[Slot ID]
    586 --And [A].[Transaction ID]=[B].[Transaction ID]+1
    587 AND [B].[Transaction ID]=  (SELECT Min(Cast([Transaction ID] as int)) as [Transaction ID] FROM #temp_Data AS [C]
    588 WHERE [A].[Slot ID]=[C].[Slot ID]
    589 GROUP BY [Slot ID])
    590 INNER JOIN sys.columns [D] ON  [object_id]=object_id('' + @SchemaName_n_TableName + '')
    591 AND [A].[Fieldname]=D.[name]
    592 WHERE ISNULL([A].[FieldValue],'')=ISNULL([B].[FieldValue],'')
    593 AND A.[Transaction ID] NOT IN (SELECT Min(Cast([Transaction ID] as int)) as [Transaction ID] FROM #temp_Data AS [C]
    594 WHERE [A].[Slot ID]=[C].[Slot ID]
    595 GROUP BY [Slot ID])
    596 )
    597  
    598 ,CTEUpdateQuery AS (SELECT 'UPDATE ' +  @SchemaName_n_TableName +  ' SET ' + LEFT(
    599 STUFF((SELECT ' ' + ISNULL([Field],'')+ ' ' FROM CTE B 
    600 WHERE A.[Slot ID]=B.[Slot ID] AND A.[Transaction ID]=B.[Transaction ID] And B.[Type]='D' FOR XML PATH('') ),1,1,''), 
    601  
    602 LEN(STUFF((SELECT ' ' +ISNULL([Field],'')+ ' ' FROM CTE B 
    603 WHERE A.[Slot ID]=B.[Slot ID] AND A.[Transaction ID]=B.[Transaction ID] And B.[Type]='D' FOR XML PATH('') ),1,1,'') )-2)
    604  
    605 + '  WHERE  ' +
    606  
    607 LEFT(STUFF((SELECT ' ' +ISNULL([Field],'')+ ' ' FROM CTE C 
    608 WHERE A.[Slot ID]=C.[Slot ID] AND A.[Transaction ID]=C.[Transaction ID] And C.[Type]='S' FOR XML PATH('') ),1,1,'') ,
    609  
    610 LEN(STUFF((SELECT ' ' +ISNULL([Field],'')+ ' ' FROM CTE C 
    611 WHERE A.[Slot ID]=C.[Slot ID] AND A.[Transaction ID]=C.[Transaction ID] And C.[Type]='S' FOR XML PATH('') ),1,1,''))-4)
    612 AS [Update Statement],
    613 [Slot ID]
    614 ,[Transaction ID]
    615 ,Rowlogcontents
    616 ,[A].[NonID]
    617 FROM CTE A
    618 GROUP BY [Slot ID]
    619 ,[Transaction ID]
    620 ,Rowlogcontents
    621 ,[A].[NonID] )
    622  
    623 INSERT INTO #temp_Data 
    624 SELECT 'Update Statement',ISNULL([Update Statement],''),[Rowlogcontents],[Transaction ID],[Slot ID],[NonID] FROM CTEUpdateQuery
    625  
    626 /**************************/
    627 --Create the column name in the same order to do pivot table.
    628 DECLARE @FieldName VARCHAR(max)
    629 SET @FieldName = STUFF(
    630 (
    631 SELECT ',' + CAST(QUOTENAME([Name]) AS VARCHAR(MAX)) FROM syscolumns WHERE id=object_id('' + @SchemaName_n_TableName + '')
    632  
    633 FOR XML PATH('')
    634 ), 1, 1, '')
    635  
    636 --Finally did pivot table and got the data back in the same format.
    637 --The [Update Statement] column will give you the query that you can execute in case of recovery.
    638 SET @sql = 'SELECT ' + @FieldName  + ',[Update Statement] FROM #temp_Data 
    639 PIVOT (Min([FieldValue]) FOR FieldName IN (' + @FieldName  + ',[Update Statement])) AS pvt
    640 Where [Transaction ID] NOT In (Select Min(Cast([Transaction ID] as int)) as [Transaction ID] from #temp_Data
    641 Group By [Slot ID]) ORDER BY Convert(int,[Slot ID]),Convert(int,[Transaction ID])'
    642 Print @sql
    643 EXEC sp_executesql @sql
    644  
    645 GO
    646 --Execute the procedure like
    647 --Recover_Modified_Data_Proc 'Database name''Schema.table name','Date from' ,'Date to'
    648  
    649 ----EXAMPLE #1 : FOR ALL MODIFIED RECORDS
    650 EXEC Recover_Modified_Data_Proc 'test','dbo.Student'
    651 GO
    652 --EXAMPLE #2 : FOR ANY SPECIFIC DATE RANGE
    653 EXEC Recover_Modified_Data_Proc  'test','dbo.Student','2000/01/01','9999/12/31'
    654 --It will give you the result of all modified records.
    View Code
     
  • 相关阅读:
    共享文件时提示“将安全性信息应用到以下对象时发生错误”
    ROS 5.x自动定时备份并发送到邮箱(实用)
    Android检测网络是否正常代码!
    win7提示“User Profile Service服务未能登录”
    Android-修改TabWidget字体大小颜色及对齐
    Android 实现分页(使用TabWidget/TabHost)
    解决在ScrollView中套用ListView显示不正常
    Android中finish掉其它的Activity
    Android中如何控制元素的显示隐藏?
    <jQuery> 十一. 基本动画(显示, 隐藏, 滑入, 滑出, 淡入淡出)
  • 原文地址:https://www.cnblogs.com/xiaozizi/p/5953449.html
Copyright © 2020-2023  润新知