原文链接: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

转载于:https://www.cnblogs.com/xiaozizi/p/5953449.html

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

  1. LANDESK操作系统部署时,网卡启动后,自动进入pe选择菜单,而不用按F8 进行选择启动菜单的情况下,如何操作?...

    LANDESK操作系统部署时,网卡启动后,自动进入pe选择菜单,而不用按F8 进行选择启动菜单的情况下,如何操作? 设置方法: 使用PXE的控制队列 PXE的控制队列是另一个远程图像PXE的部署操作系 ...

  2. // D:\SaveLog\_SaveLog.dpr立即备份晓亮的电脑操作记录热键(快捷键) F11由于原来的 AutoIt 杀毒软件总是误报没办法只好麻烦一点用 Delphi XE4 做了...

    // D:\SaveLog\_SaveLog.dpr立即备份晓亮的电脑操作记录 热键(快捷键) F11由于原来的 AutoIt 杀毒软件总是误报没办法只好麻烦一点用 Delphi 各种版本 做了 (我 ...

  3. _SaveLog.dpr立即备份晓亮的电脑操作记录热键(快捷键) F11由于原来的 AutoIt 杀毒软件总是误报...

    {*******************************************************} {                                          ...

  4. ;按F11立即备份晓亮的电脑操作记录并打开记录.AU3 (AutoIt)

    2013-10-18  11:52:34 #region ;**** 参数创建于 ACNWrapper_GUI **** #PRE_UseUpx=n #PRE_Res_requestedExecuti ...

  5. 【黄啊码】MySQL入门—17、在没有备份的情况下,如何恢复数据库数据?

    大家好!我是黄啊码,MySQL的入门篇已经讲到第16个课程了,今天我们继续讲讲大白篇系列--科技与狠活之恢复数据库 在没做数据库备份,没有开启使用 Binlog 的情况下,尽可能地找回数据. 今天的内 ...

  6. SQLServer中的数据库备份恢复并重命名

    在做数据库备份时常常需要将A数据库备份,并恢复成一模一样的B数据库,但是在restore的时候SQL Server会自动式别.bak文件的数据文件和日志文件的逻辑名称(一般情况下是 A.mdf 和 A ...

  7. mysql设置停止二进制文件的操作_window下使用二进制文件对mysql数据库备份及恢复(再现用户操作)...

    1,查看二进制日志文件是否启用 mysql>show variables like 'log_bin'; 如果未启用,则启用二进制文件(默认是关闭的 ) 2,在mysql的配置文件[mysqld ...

  8. MySQL将表中的yes改成no_mysql在不需要改程序的情况下通过操作数据库对单表数据量大的表进行分表...

    1.为什么要分表? 数据库数据越来越大,随之而来的是单个表中数据太多.以至于查询速度变慢,而且由于表的锁机制导致应用操作也搜到严重影响,出现了数据库性能瓶颈. mysql中有一种机制是表锁定和行锁定, ...

  9. vertica 数据库 linux,CentOS 7下安装vertica记录

    CentOS 7下安装vertica记录 1.    安装好centeros 并更新 Centeros安装就不说了,安装完之后联网环境下 yum update.更新下,使得那些包都是新的.(要想用中文 ...

最新文章

  1. 初探JavaScript魅力1
  2. centos6.4安装java,CentOS6.4下YUM安装MySQL和JDK和Tomcat
  3. Python计算机视觉:第六章 图像聚类
  4. 计算机技术会议排名,计算机学科会议排名
  5. 【存储过程】Merge Into语句实现Insert/Update在Oracle中的应用
  6. mysql表变量临时表_表变量和临时表详解
  7. Spring Data JPA 从入门到精通~javax.persistence概况介绍
  8. 实践2.2 内核模块编译
  9. php后缀名隐藏,php隐藏后缀名的方法是什么
  10. r语言结构方程模型可视化_R语言时间序列分析(二):ts对象及其可视化
  11. request.form 和 Request.QueryString 区别
  12. 廖雪峰python笔记
  13. axure原型图手机界面尺寸
  14. 视频去水印工具-视频去水印哪个软件好用免费
  15. A Survey of Zero-Shot Learning: Settings, Methods, and Applications [reading notes]
  16. 中国大学MOOC-陈越、何钦铭-数据结构-2020春期中考试
  17. 【Java】Map类
  18. Java中xml转义字符和gt,gte,lt,lte缩写
  19. vue animate bounceInRight 只执行一遍
  20. 惟伊·京汉方内部启动会圆满成功

热门文章

  1. EIGRP的successor与fessible successor
  2. Flex实现分页显示功能(mx:DataGrid)
  3. 工作中让你提建议怎么办?
  4. Querying an unbounded table ‘myhive.db1.datagen‘ in batch mode is not allowed
  5. CHUNGHOP k-6868万能空调遥控器的自己家用电器的代码+自己家里的宽带账号+机顶盒型号+桌子+椅子+垫子高度
  6. ubnutu18.10拔除硬盘后进行recovery mode
  7. ubuntu16.04下面xfce4没有声音
  8. SAFESEH 映像的模块不安全
  9. android word转html标签,如何将Word转换为网页html格式的方法(附代码清理方法)
  10. Machine Learning之Python篇(二)