[SQL][Script]救回被誤刪的資料
在 SQL Server 下有時會發現資料不知道被誰刪除的,如果我們可以事先知道有刪除的動作,那麼透過 Audit 、SQL Trace 或者是 Trigger 等機制,那麼都還有辦法做紀錄。但如果事前沒有做這些處理,那麼該怎麼來處理呢 ? 前一陣子遇到類似的狀況,但客戶雖然用 ApexSQL Log 找回被刪除的資料,但對一值想不出來這個要怎麼做。昨天跟亂馬客和 Terry 討論之後,感覺上透過 :fn_dblog 可以讀取 Log,雖然我們可以透過這個系統函數,可以把異動的動作 ( Action )和時間找出來,但資料呢 ? 看起來似乎要從紀錄中的 Binary 資料轉出來,但這方便似乎沒有相關資料說明。
透過亂馬客的協助和修正,他更改了一個版本的查看修改資料的命令。目前這個版本必須要在 Recovery Mode 是 Full 或者是 Bulk Insert 下來使用,測試起來如果是下 SQL 指令刪除資料的話 ( DELETE ),那麼透過這個 Stored Procedure,可以將被刪除的資料給找出來;但如果您的資料庫的 Recovery Mode 是 Simple 的狀況下,在還沒有發生 Checkpoint 之前,都還可以找到資料,但倘若發生 Checkpoint 的話,那麼就沒有機會了。
整個 Stored Procedure 有點複雜,中間有一大段都是 Binary 在轉碼處理,原作者的這一段有點小缺陷,並沒有辦法支援區分大小寫的資料庫,有些拼字要調整;另外針對有些時候,我們可能並不是直接下 SQL 指令刪除,可能會針對 ADO 的 Result Set 去操作,那麼他可能會把 SQL 指令變成 sp_executesql 'DELETE FROM …' 的指令,亂馬克也協助修正了這個問題,有興趣的朋友可以參考以下的程式碼。
   1:  
		
		2: -- http://raresql.com/2012/10/24/sql-server-how-to-find-who-deleted-what-records-at-what-time/
3: -- Script Name: Recover_Deleted_Data_With_UID_Date_Time_Proc
4: -- Script Type : Recovery Procedure
5: -- Develop By: Muhammad Imran
6: -- Date Created: 24 Oct 2012
7: -- Modify Date:
8: -- Version : 1.2
9: -- Notes :
10: -- 1.修改不傳入DB Name改取自DB_NAME()
11: -- 2.修改 sysname DataType to nvarchar(128)
12: -- 3.使用SUSER_SNAME 代替 sysusers join 找出操作人員
13: -- 4.修正在區分大小寫的DB中,會發生的錯誤
14: -- 5.加入判斷,如下是透過SSMS的Edit Row時,刪除的[Transaction Name] 會用 user_transaction [Transaction Name] IN ('DELETE', 'user_transaction')
15: -- Example: EXEC Recover_Deleted_Data_With_UID_Date_Time_Proc 'dbo.tbl_Sample'
16: ALTER PROCEDURE Recover_Deleted_Data_With_UID_Date_Time_Proc
17: @SchemaName_n_TableName NVARCHAR(MAX) ,
18: @Date_From DATETIME = '1900/01/01' ,
19: @Date_To DATETIME = '9999/12/31'
20: AS
21: DECLARE @RowLogContents VARBINARY(8000)
22: DECLARE @TransactionID NVARCHAR(MAX)
23: DECLARE @AllocUnitID BIGINT
24: DECLARE @AllocUnitName NVARCHAR(MAX)
25: DECLARE @SQL NVARCHAR(MAX)
26: DECLARE @Compatibility_Level INT
27: DECLARE @Database_Name NVARCHAR(MAX )
  28:  
		
		29: SET @Database_Name = DB_NAME()
30: SELECT @Compatibility_Level = dtb.compatibility_level
31: FROM master.sys.databases AS dtb
32: WHERE dtb.name = @Database_Name
  33:  
		
		34: IF ISNULL(@Compatibility_Level, 0) <= 80
35: BEGIN
36: RAISERROR('The compatibility level should be equal to or greater SQL SERVER 2005 (90)',16,1)
37: RETURN
38: END
  39:  
		
		40: IF ( SELECT COUNT(*)
41: FROM INFORMATION_SCHEMA.TABLES
42: WHERE [TABLE_SCHEMA] + '.' + [TABLE_NAME] = @SchemaName_n_TableName
  43:        ) = 0 
		
		44: BEGIN
45: RAISERROR('Could not found the table in the defined database',16,1)
46: RETURN
47: END
  48:  
		
		49: DECLARE @bitTable TABLE
  50:         (
		
		51: [ID] INT ,
52: [Bitvalue] INT
  53:         )
		
		54: --Create table to set the bit position of one byte.
  55:  
		
		56: INSERT INTO @bitTable
57: SELECT 0 ,
  58:                     2
		
		59: UNION ALL
60: SELECT 1 ,
  61:                     2
		
		62: UNION ALL
63: SELECT 2 ,
  64:                     4
		
		65: UNION ALL
66: SELECT 3 ,
  67:                     8
		
		68: UNION ALL
69: SELECT 4 ,
  70:                     16
		
		71: UNION ALL
72: SELECT 5 ,
  73:                     32
		
		74: UNION ALL
75: SELECT 6 ,
  76:                     64
		
		77: UNION ALL
78: SELECT 7 ,
  79:                     128
		
		
  80:  
		
		81: --Create table to collect the row data.
82: DECLARE @DeletedRecords TABLE
  83:         (
		
		84: [Row ID] INT IDENTITY(1, 1) ,
  85:           [RowLogContents] VARBINARY(8000) ,
		
		
  86:           [AllocUnitId] BIGINT ,
		
		87: [Transaction ID] NVARCHAR(MAX) ,
88: [FixedLengthData] SMALLINT ,
89: [TotalNoOfCols] SMALLINT ,
90: [NullBitMapLength] SMALLINT ,
  91:           [NullBytes] VARBINARY(8000) ,
		
		92: [TotalNoofVarCols] SMALLINT ,
  93:           [ColumnOffsetArray] VARBINARY(8000) ,
		
		94: [VarColumnStart] SMALLINT ,
95: [Slot ID] INT ,
96: [NullBitMap] VARCHAR(MAX)
  97:         )
		
		98: --Create a common table expression to get all the row data plus how many bytes we have for each row.
  99: ;
		
		100: WITH RowData
101: AS ( SELECT [RowLog Contents 0] AS [RowLogContents] ,
102: [AllocUnitID] AS [AllocUnitID] ,
103: [Transaction ID] AS [Transaction ID]
 104:  
		
		105: --[Fixed Length Data] = Substring (RowLog content 0, Status Bit A+ Status Bit B + 1,2 bytes)
 106:                             ,
		
		107: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
108: 2 + 1, 2)))) AS [FixedLengthData] --@FixedLengthData
 109:  
		
		110: -- [TotalnoOfCols] = Substring (RowLog content 0, [Fixed Length Data] + 1,2 bytes)
 111:                             ,
		
		112: CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
113: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 114:                                                               2 + 1, 2)))) + 1,
		
		115: 2)))) AS [TotalNoOfCols]
 116:  
		
		117: --[NullBitMapLength]=ceiling([Total No of Columns] /8.0)
 118:                             ,
		
		119: CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
120: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 121:                                                               2 + 1, 2)))) + 1,
		
		122: 2)))) / 8.0)) AS [NullBitMapLength]
 123:  
		
		124: --[Null Bytes] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [NullBitMapLength] )
 125:                             ,
		
		126: SUBSTRING([RowLog Contents 0],
127: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 128:                                                               2 + 1, 2)))) + 3,
		
		129: CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
130: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 131:                                                               2 + 1, 2)))) + 1,
		
		132: 2)))) / 8.0))) AS [NullBytes]
 133:  
		
		134: --[TotalNoofVarCols] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 )
 135:                             ,
		
		136: ( CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN (
 137:                                         0x10, 0x30, 0x70 )
		
		138: THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
139: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 140:                                                               2 + 1, 2)))) + 3
		
		141: + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
142: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 143:                                                               2 + 1, 2)))) + 1,
		
		
 144:                                                               2)))) / 8.0)), 2))))
		
		145: ELSE NULL
146: END ) AS [TotalNoofVarCols]
 147:  
		
		148: --[ColumnOffsetArray]= Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 , [TotalNoofVarCols]*2 )
 149:                             ,
		
		150: ( CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN (
 151:                                         0x10, 0x30, 0x70 )
		
		152: THEN SUBSTRING([RowLog Contents 0],
153: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 154:                                                               2 + 1, 2)))) + 3
		
		155: + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
156: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 157:                                                               2 + 1, 2)))) + 1,
		
		
 158:                                                               2)))) / 8.0))
		
		
 159:                                                   + 2,
		
		160: ( CASE WHEN SUBSTRING([RowLog Contents 0],
161: 1, 1) IN ( 0x10,
 162:                                                               0x30, 0x70 )
		
		163: THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
164: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 165:                                                               2 + 1, 2)))) + 3
		
		166: + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
167: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 168:                                                               2 + 1, 2)))) + 1,
		
		
 169:                                                               2)))) / 8.0)), 2))))
		
		170: ELSE NULL
171: END ) * 2)
172: ELSE NULL
173: END ) AS [ColumnOffsetArray]
 174:  
		
		175: -- Variable column Start = Status Bit A+ Status Bit B + [Fixed Length Data] + [Null Bitmap length] + 2+([TotalNoofVarCols]*2)
 176:                             ,
		
		177: CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) IN (
 178:                                       0x10, 0x30, 0x70 )
		
		179: THEN ( CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 180:                                                               2 + 1, 2)))) + 4
		
		181: + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
182: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 183:                                                               2 + 1, 2)))) + 1,
		
		
 184:                                                               2)))) / 8.0))
		
		185: + ( ( CASE WHEN SUBSTRING([RowLog Contents 0],
186: 1, 1) IN ( 0x10,
 187:                                                               0x30, 0x70 )
		
		188: THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
189: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 190:                                                               2 + 1, 2)))) + 3
		
		191: + CONVERT(INT, CEILING(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
192: CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0],
 193:                                                               2 + 1, 2)))) + 1,
		
		
 194:                                                               2)))) / 8.0)), 2))))
		
		195: ELSE NULL
196: END ) * 2 ) )
197: ELSE NULL
198: END AS [VarColumnStart] ,
 199:                             [Slot ID]
		
		200: FROM sys.fn_dblog(NULL, NULL)
201: WHERE AllocUnitId IN (
202: SELECT [Allocation_unit_id]
203: FROM sys.allocation_units allocunits
204: INNER JOIN sys.partitions partitions ON ( allocunits.type IN (
 205:                                                               1, 3 )
		
		206: AND partitions.hobt_id = allocunits.container_id
 207:                                                               )
		
		208: OR ( allocunits.type = 2
209: AND partitions.partition_id = allocunits.container_id
 210:                                                               )
		
		211: WHERE object_id = OBJECT_ID(''
 212:                                                           + @SchemaName_n_TableName
		
		213: + '') )
214: AND Context IN ( 'LCX_MARK_AS_GHOST', 'LCX_HEAP' )
215: AND Operation IN ( 'LOP_DELETE_ROWS' )
216: AND SUBSTRING([RowLog Contents 0], 1, 1) IN ( 0x10,
 217:                                                               0x30, 0x70 )
		
		
 218:  
		
		219: /*Use this subquery to filter the date*/
220: AND [TRANSACTION ID] IN (
221: SELECT DISTINCT
222: [TRANSACTION ID]
223: FROM sys.fn_dblog(NULL, NULL)
224: WHERE Context IN ( 'LCX_NULL' )
225: AND Operation IN ( 'LOP_BEGIN_XACT' )
226: AND [Transaction Name] IN ('DELETE', 'user_transaction')
227: AND CONVERT(NVARCHAR(11), [Begin Time]) BETWEEN @Date_From
228: AND
 229:                                                               @Date_To)
		
		
 230:                  ),
		
		
 231:  
		
		232: --Use this technique to repeate the row till the no of bytes of the row.
 233:             N1 ( n )
		
		234: AS ( SELECT 1
235: UNION ALL
236: SELECT 1
 237:                  ),
		
		
 238:             N2 ( n )
		
		239: AS ( SELECT 1
240: FROM N1 AS X ,
241: N1 AS Y
 242:                  ),
		
		
 243:             N3 ( n )
		
		244: AS ( SELECT 1
245: FROM N2 AS X ,
246: N2 AS Y
 247:                  ),
		
		
 248:             N4 ( n )
		
		249: AS ( SELECT ROW_NUMBER() OVER ( ORDER BY X.n )
250: FROM N3 AS X ,
251: N3 AS Y
 252:                  )
		
		253: INSERT INTO @DeletedRecords
254: SELECT RowLogContents ,
 255:                         [AllocUnitID] ,
		
		256: [Transaction ID] ,
 257:                         [FixedLengthData] ,
		
		
 258:                         [TotalNoOfCols] ,
		
		
 259:                         [NullBitMapLength] ,
		
		
 260:                         [NullBytes] ,
		
		
 261:                         [TotalNoofVarCols] ,
		
		
 262:                         [ColumnOffsetArray] ,
		
		
 263:                         [VarColumnStart] ,
		
		
 264:                         [Slot ID]
		
		265: ---Get the Null value against each column (1 means null zero means not null)
 266:                         ,
		
		267: [NullBitMap] = ( REPLACE(STUFF(( SELECT
268: ','
269: + ( CASE
270: WHEN [ID] = 0
271: THEN CONVERT(NVARCHAR(1), ( SUBSTRING(NullBytes,
 272:                                                               n, 1) % 2 ))
		
		273: ELSE CONVERT(NVARCHAR(1), ( ( SUBSTRING(NullBytes,
 274:                                                               n, 1)
		
		
 275:                                                               / [Bitvalue] )
		
		
 276:                                                               % 2 ))
		
		277: END ) --as [NullBitMap]
278: FROM N4 AS Nums
279: JOIN RowData AS C ON n <= NullBitMapLength
280: CROSS JOIN @bitTable
281: WHERE
 282:                                                               C.[RowLogContents] = D.[RowLogContents]
		
		283: ORDER BY [RowLogContents] ,
284: n ASC
285: FOR
286: XML PATH('')
287: ), 1, 1, ''), ',', '') )
288: FROM RowData D
 289:  
		
		290: IF ( SELECT COUNT(*)
291: FROM @DeletedRecords
 292:        ) = 0 
		
		293: BEGIN
294: RAISERROR('There is no data in the log as per the search criteria',16,1)
295: RETURN
296: END
 297:  
		
		298: DECLARE @ColumnNameAndData TABLE
 299:         (
		
		300: [Transaction ID] VARCHAR(100) ,
301: [Row ID] INT ,
302: [RowLogContents] VARBINARY(MAX) ,
 303:           [NAME] nvarchar(128) ,
		
		304: [nullbit] SMALLINT ,
305: [leaf_offset] SMALLINT ,
306: [length] SMALLINT ,
 307:           [system_type_id] TINYINT ,
		
		
 308:           [bitpos] TINYINT ,
		
		
 309:           [xprec] TINYINT ,
		
		
 310:           [xscale] TINYINT ,
		
		311: [is_null] INT ,
312: [Column value Size] INT ,
313: [Column Length] INT ,
314: [hex_Value] VARBINARY(MAX) ,
315: [Slot ID] INT ,
316: [Update] INT
 317:         )
		
		
 318:  
		
		319: --Create common table expression and join it with the rowdata table
320: -- to get each column details
321: /*This part is for variable data columns*/
 322: --@RowLogContents, 
		
		
 323: --(col.columnOffValue - col.columnLength) + 1,
		
		
 324: --col.columnLength
		
		
 325: --)
		
		326: INSERT INTO @ColumnNameAndData
327: SELECT [Transaction ID] ,
328: [Row ID] ,
 329:                     RowLogContents ,
		
		
 330:                     NAME ,
		
		331: cols.leaf_null_bit AS nullbit ,
 332:                     leaf_offset ,
		
		333: ISNULL(syscolumns.length, cols.max_length) AS [length] ,
 334:                     cols.system_type_id ,
		
		335: cols.leaf_bit_position AS bitpos ,
336: ISNULL(syscolumns.xprec, cols.precision) AS xprec ,
337: ISNULL(syscolumns.xscale, cols.scale) AS xscale ,
338: SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) AS is_null ,
339: ( CASE WHEN leaf_offset < 1
340: AND SUBSTRING([NullBitMap], cols.leaf_null_bit,
 341:                                               1) = 0
		
		342: THEN ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 343:                                                               ( 2
		
		
 344:                                                               * leaf_offset
		
		
 345:                                                               * -1 ) - 1, 2)))) > 30000
		
		346: THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 347:                                                               ( 2
		
		
 348:                                                               * leaf_offset
		
		
 349:                                                               * -1 ) - 1, 2))))
		
		
 350:                                             - POWER(2, 15)
		
		351: ELSE CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 352:                                                               ( 2
		
		
 353:                                                               * leaf_offset
		
		
 354:                                                               * -1 ) - 1, 2))))
		
		355: END )
356: END ) AS [Column value Size] ,
357: ( CASE WHEN leaf_offset < 1
358: AND SUBSTRING([NullBitMap], cols.leaf_null_bit,
 359:                                               1) = 0
		
		360: THEN ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 361:                                                               ( 2
		
		
 362:                                                               * leaf_offset
		
		
 363:                                                               * -1 ) - 1, 2)))) > 30000
		
		364: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 365:                                                               ( 2
		
		
 366:                                                               * ( ( leaf_offset
		
		
 367:                                                               * -1 ) - 1 ) )
		
		
 368:                                                               - 1, 2)))), 0),
		
		
 369:                                                        [VarColumnStart]) < 30000
		
		370: THEN ( CASE WHEN [system_type_id] IN (
371: 35, 34, 99 ) THEN 16
372: ELSE 24
373: END )
374: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 375:                                                               ( 2
		
		
 376:                                                               * leaf_offset
		
		
 377:                                                               * -1 ) - 1, 2)))) > 30000
		
		378: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 379:                                                               ( 2
		
		
 380:                                                               * ( ( leaf_offset
		
		
 381:                                                               * -1 ) - 1 ) )
		
		
 382:                                                               - 1, 2)))), 0),
		
		
 383:                                                        [VarColumnStart]) > 30000
		
		384: THEN ( CASE WHEN [system_type_id] IN (
385: 35, 34, 99 ) THEN 16
386: ELSE 24
387: END ) --24
388: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 389:                                                               ( 2
		
		
 390:                                                               * leaf_offset
		
		
 391:                                                               * -1 ) - 1, 2)))) < 30000
		
		392: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 393:                                                               ( 2
		
		
 394:                                                               * ( ( leaf_offset
		
		
 395:                                                               * -1 ) - 1 ) )
		
		
 396:                                                               - 1, 2)))), 0),
		
		
 397:                                                        [VarColumnStart]) < 30000
		
		398: THEN ( CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 399:                                                               ( 2
		
		
 400:                                                               * leaf_offset
		
		
 401:                                                               * -1 ) - 1, 2))))
		
		402: - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 403:                                                               ( 2
		
		
 404:                                                               * ( ( leaf_offset
		
		
 405:                                                               * -1 ) - 1 ) )
		
		
 406:                                                               - 1, 2)))), 0),
		
		
 407:                                                        [VarColumnStart]) )
		
		408: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 409:                                                               ( 2
		
		
 410:                                                               * leaf_offset
		
		
 411:                                                               * -1 ) - 1, 2)))) < 30000
		
		412: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 413:                                                               ( 2
		
		
 414:                                                               * ( ( leaf_offset
		
		
 415:                                                               * -1 ) - 1 ) )
		
		
 416:                                                               - 1, 2)))), 0),
		
		
 417:                                                        [VarColumnStart]) > 30000
		
		418: THEN POWER(2, 15)
419: + CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 420:                                                               ( 2
		
		
 421:                                                               * leaf_offset
		
		
 422:                                                               * -1 ) - 1, 2))))
		
		423: - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 424:                                                               ( 2
		
		
 425:                                                               * ( ( leaf_offset
		
		
 426:                                                               * -1 ) - 1 ) )
		
		
 427:                                                               - 1, 2)))), 0),
		
		
 428:                                                      [VarColumnStart])
		
		429: END )
430: END ) AS [Column Length] ,
431: ( CASE WHEN SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) = 1
432: THEN NULL
433: ELSE SUBSTRING(RowLogContents,
434: ( ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 435:                                                               ( 2
		
		
 436:                                                               * leaf_offset
		
		
 437:                                                               * -1 ) - 1, 2)))) > 30000
		
		438: THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 439:                                                               ( 2
		
		
 440:                                                               * leaf_offset
		
		
 441:                                                               * -1 ) - 1, 2))))
		
		
 442:                                                         - POWER(2, 15)
		
		443: ELSE CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 444:                                                               ( 2
		
		
 445:                                                               * leaf_offset
		
		
 446:                                                               * -1 ) - 1, 2))))
		
		447: END )
448: - ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 449:                                                               ( 2
		
		
 450:                                                               * leaf_offset
		
		
 451:                                                               * -1 ) - 1, 2)))) > 30000
		
		452: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 453:                                                               ( 2
		
		
 454:                                                               * ( ( leaf_offset
		
		
 455:                                                               * -1 ) - 1 ) )
		
		
 456:                                                               - 1, 2)))), 0),
		
		
 457:                                                               [VarColumnStart]) < 30000
		
		458: THEN ( CASE
459: WHEN [system_type_id] IN (
 460:                                                               35, 34, 99 )
		
		461: THEN 16
462: ELSE 24
463: END ) --24
464: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 465:                                                               ( 2
		
		
 466:                                                               * leaf_offset
		
		
 467:                                                               * -1 ) - 1, 2)))) > 30000
		
		468: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 469:                                                               ( 2
		
		
 470:                                                               * ( ( leaf_offset
		
		
 471:                                                               * -1 ) - 1 ) )
		
		
 472:                                                               - 1, 2)))), 0),
		
		
 473:                                                               [VarColumnStart]) > 30000
		
		474: THEN ( CASE
475: WHEN [system_type_id] IN (
 476:                                                               35, 34, 99 )
		
		477: THEN 16
478: ELSE 24
479: END ) --24
480: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 481:                                                               ( 2
		
		
 482:                                                               * leaf_offset
		
		
 483:                                                               * -1 ) - 1, 2)))) < 30000
		
		484: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 485:                                                               ( 2
		
		
 486:                                                               * ( ( leaf_offset
		
		
 487:                                                               * -1 ) - 1 ) )
		
		
 488:                                                               - 1, 2)))), 0),
		
		
 489:                                                               [VarColumnStart]) < 30000
		
		490: THEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 491:                                                               ( 2
		
		
 492:                                                               * leaf_offset
		
		
 493:                                                               * -1 ) - 1, 2))))
		
		494: - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 495:                                                               ( 2
		
		
 496:                                                               * ( ( leaf_offset
		
		
 497:                                                               * -1 ) - 1 ) )
		
		
 498:                                                               - 1, 2)))), 0),
		
		
 499:                                                               [VarColumnStart])
		
		500: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 501:                                                               ( 2
		
		
 502:                                                               * leaf_offset
		
		
 503:                                                               * -1 ) - 1, 2)))) < 30000
		
		504: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 505:                                                               ( 2
		
		
 506:                                                               * ( ( leaf_offset
		
		
 507:                                                               * -1 ) - 1 ) )
		
		
 508:                                                               - 1, 2)))), 0),
		
		
 509:                                                               [VarColumnStart]) > 30000
		
		510: THEN POWER(2, 15)
511: + CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 512:                                                               ( 2
		
		
 513:                                                               * leaf_offset
		
		
 514:                                                               * -1 ) - 1, 2))))
		
		515: - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 516:                                                               ( 2
		
		
 517:                                                               * ( ( leaf_offset
		
		
 518:                                                               * -1 ) - 1 ) )
		
		
 519:                                                               - 1, 2)))), 0),
		
		
 520:                                                               [VarColumnStart])
		
		521: END ) ) + 1,
522: ( CASE WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 523:                                                               ( 2
		
		
 524:                                                               * leaf_offset
		
		
 525:                                                               * -1 ) - 1, 2)))) > 30000
		
		526: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 527:                                                               ( 2
		
		
 528:                                                               * ( ( leaf_offset
		
		
 529:                                                               * -1 ) - 1 ) )
		
		
 530:                                                               - 1, 2)))), 0),
		
		
 531:                                                               [VarColumnStart]) < 30000
		
		532: THEN ( CASE WHEN [system_type_id] IN (
 533:                                                               35, 34, 99 )
		
		534: THEN 16
535: ELSE 24
536: END ) --24
537: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 538:                                                               ( 2
		
		
 539:                                                               * leaf_offset
		
		
 540:                                                               * -1 ) - 1, 2)))) > 30000
		
		541: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 542:                                                               ( 2
		
		
 543:                                                               * ( ( leaf_offset
		
		
 544:                                                               * -1 ) - 1 ) )
		
		
 545:                                                               - 1, 2)))), 0),
		
		
 546:                                                               [VarColumnStart]) > 30000
		
		547: THEN ( CASE WHEN [system_type_id] IN (
 548:                                                               35, 34, 99 )
		
		549: THEN 16
550: ELSE 24
551: END ) --24
552: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 553:                                                               ( 2
		
		
 554:                                                               * leaf_offset
		
		
 555:                                                               * -1 ) - 1, 2)))) < 30000
		
		556: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 557:                                                               ( 2
		
		
 558:                                                               * ( ( leaf_offset
		
		
 559:                                                               * -1 ) - 1 ) )
		
		
 560:                                                               - 1, 2)))), 0),
		
		
 561:                                                               [VarColumnStart]) < 30000
		
		562: THEN ABS(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 563:                                                               ( 2
		
		
 564:                                                               * leaf_offset
		
		
 565:                                                               * -1 ) - 1, 2))))
		
		566: - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 567:                                                               ( 2
		
		
 568:                                                               * ( ( leaf_offset
		
		
 569:                                                               * -1 ) - 1 ) )
		
		
 570:                                                               - 1, 2)))), 0),
		
		
 571:                                                               [VarColumnStart]))
		
		572: WHEN CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 573:                                                               ( 2
		
		
 574:                                                               * leaf_offset
		
		
 575:                                                               * -1 ) - 1, 2)))) < 30000
		
		576: AND ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 577:                                                               ( 2
		
		
 578:                                                               * ( ( leaf_offset
		
		
 579:                                                               * -1 ) - 1 ) )
		
		
 580:                                                               - 1, 2)))), 0),
		
		
 581:                                                               [VarColumnStart]) > 30000
		
		582: THEN POWER(2, 15)
583: + CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 584:                                                               ( 2
		
		
 585:                                                               * leaf_offset
		
		
 586:                                                               * -1 ) - 1, 2))))
		
		587: - ISNULL(NULLIF(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([ColumnOffsetArray],
 588:                                                               ( 2
		
		
 589:                                                               * ( ( leaf_offset
		
		
 590:                                                               * -1 ) - 1 ) )
		
		
 591:                                                               - 1, 2)))), 0),
		
		
 592:                                                               [VarColumnStart])
		
		593: END ))
594: END ) AS hex_Value ,
 595:                     [Slot ID] ,
		
		
 596:                     0
		
		597: FROM @DeletedRecords A
598: INNER JOIN sys.allocation_units allocunits ON A.[AllocUnitId] = allocunits.[Allocation_Unit_Id]
599: INNER JOIN sys.partitions partitions ON ( allocunits.type IN (
 600:                                                               1, 3 )
		
		601: AND partitions.hobt_id = allocunits.container_id
 602:                                                             )
		
		603: OR ( allocunits.type = 2
604: AND partitions.partition_id = allocunits.container_id
 605:                                                               )
		
		606: INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id
607: LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id
608: AND syscolumns.colid = cols.partition_column_id
609: WHERE leaf_offset < 0
610: UNION
611: /*This part is for fixed data columns*/
612: SELECT [Transaction ID] ,
613: [Row ID] ,
 614:                     RowLogContents ,
		
		
 615:                     NAME ,
		
		616: cols.leaf_null_bit AS nullbit ,
 617:                     leaf_offset ,
		
		618: ISNULL(syscolumns.length, cols.max_length) AS [length] ,
 619:                     cols.system_type_id ,
		
		620: cols.leaf_bit_position AS bitpos ,
621: ISNULL(syscolumns.xprec, cols.precision) AS xprec ,
622: ISNULL(syscolumns.xscale, cols.scale) AS xscale ,
623: SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) AS is_null ,
624: ( SELECT TOP 1
625: ISNULL(SUM(CASE WHEN C.leaf_offset > 1
626: THEN max_length
627: ELSE 0
628: END), 0)
629: FROM sys.system_internals_partition_columns C
630: WHERE cols.partition_id = C.partition_id
631: AND C.leaf_null_bit < cols.leaf_null_bit
632: ) + 5 AS [Column value Size] ,
633: syscolumns.length AS [Column Length] ,
634: CASE WHEN SUBSTRING([NullBitMap], cols.leaf_null_bit, 1) = 1
635: THEN NULL
636: ELSE SUBSTRING(RowLogContents,
637: ( SELECT TOP 1
638: ISNULL(SUM(CASE
639: WHEN C.leaf_offset > 1
640: AND C.leaf_bit_position = 0
641: THEN max_length
642: ELSE 0
643: END), 0)
644: FROM sys.system_internals_partition_columns C
645: WHERE cols.partition_id = C.partition_id
646: AND C.leaf_null_bit < cols.leaf_null_bit
 647:                                         ) + 5, syscolumns.length)
		
		648: END AS hex_Value ,
 649:                     [Slot ID] ,
		
		
 650:                     0
		
		651: FROM @DeletedRecords A
652: INNER JOIN sys.allocation_units allocunits ON A.[AllocUnitId] = allocunits.[Allocation_Unit_Id]
653: INNER JOIN sys.partitions partitions ON ( allocunits.type IN (
 654:                                                               1, 3 )
		
		655: AND partitions.hobt_id = allocunits.container_id
 656:                                                             )
		
		657: OR ( allocunits.type = 2
658: AND partitions.partition_id = allocunits.container_id
 659:                                                               )
		
		660: INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id
661: LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id
662: AND syscolumns.colid = cols.partition_column_id
663: WHERE leaf_offset > 0
664: ORDER BY nullbit
 665:  
		
		666: DECLARE @BitColumnByte AS INT
667: SELECT @BitColumnByte = CONVERT(INT, CEILING(COUNT(*) / 8.0))
668: FROM @ColumnNameAndData
669: WHERE [system_type_id] = 104;
670: WITH N1 ( n )
671: AS ( SELECT 1
672: UNION ALL
673: SELECT 1
 674:                      ),
		
		
 675:                 N2 ( n )
		
		676: AS ( SELECT 1
677: FROM N1 AS X ,
678: N1 AS Y
 679:                      ),
		
		
 680:                 N3 ( n )
		
		681: AS ( SELECT 1
682: FROM N2 AS X ,
683: N2 AS Y
 684:                      ),
		
		
 685:                 N4 ( n )
		
		686: AS ( SELECT ROW_NUMBER() OVER ( ORDER BY X.n )
687: FROM N3 AS X ,
688: N3 AS Y
 689:                      ),
		
		
 690:                 CTE
		
		691: AS ( SELECT RowLogContents ,
 692:                                 [nullbit] ,
		
		693: [BitMap] = CONVERT(VARBINARY(1), CONVERT(INT, SUBSTRING(( REPLACE(STUFF(( SELECT
694: ','
695: + ( CASE
696: WHEN [ID] = 0
697: THEN CONVERT(NVARCHAR(1), ( SUBSTRING(hex_Value,
 698:                                                               n, 1) % 2 ))
		
		699: ELSE CONVERT(NVARCHAR(1), ( ( SUBSTRING(hex_Value,
 700:                                                               n, 1)
		
		
 701:                                                               / [Bitvalue] )
		
		
 702:                                                               % 2 ))
		
		703: END ) --as [NullBitMap]
704: FROM
705: N4 AS Nums
706: JOIN @ColumnNameAndData
707: AS C ON n <= @BitColumnByte
708: AND [system_type_id] = 104
709: AND bitpos = 0
710: CROSS JOIN @bitTable
711: WHERE
 712:                                                               C.[RowLogContents] = D.[RowLogContents]
		
		713: ORDER BY [RowLogContents] ,
714: n ASC
715: FOR
 716:                                                               XML
		
		717: PATH('')
718: ), 1, 1, ''),
719: ',', '') ),
 720:                                                               bitpos + 1, 1)))
		
		721: FROM @ColumnNameAndData D
722: WHERE [system_type_id] = 104
 723:                      )
		
		724: UPDATE A
725: SET [hex_Value] = [BitMap]
726: FROM @ColumnNameAndData A
727: INNER JOIN CTE B ON A.[RowLogContents] = B.[RowLogContents]
728: AND A.[nullbit] = B.[nullbit]
 729:  
		
		
 730:  
		
		731: /**************Check for BLOB DATA TYPES******************************/
732: DECLARE @Fileid INT
733: DECLARE @Pageid INT
734: DECLARE @Slotid INT
735: DECLARE @CurrentLSN INT
736: DECLARE @LinkID INT
737: DECLARE @Context VARCHAR(50)
738: DECLARE @ConsolidatedPageID VARCHAR(MAX)
739: DECLARE @LCX_TEXT_MIX VARBINARY(MAX)
 740:  
		
		741: DECLARE @temppagedata TABLE
 742:         (
		
		
 743:           [ParentObject] nvarchar(128) ,
		
		744: [Object] nvarchar(128) ,
 745:           [Field] nvarchar(128) ,
		
		746: [Value] nvarchar(128)
 747:         )
		
		
 748:  
		
		749: DECLARE @pagedata TABLE
 750:         (
		
		
 751:           [Page ID] nvarchar(128) ,
		
		752: [FILE IDS] INT ,
753: [PAGE IDS] INT ,
 754:           [AllocUnitId] BIGINT ,
		
		
 755:           [ParentObject] nvarchar(128) ,
		
		756: [Object] nvarchar(128) ,
 757:           [Field] nvarchar(128) ,
		
		758: [Value] nvarchar(128)
 759:         )
		
		
 760:  
		
		761: DECLARE @ModifiedRawData TABLE
 762:         (
		
		763: [ID] INT IDENTITY(1, 1) ,
764: [PAGE ID] VARCHAR(MAX) ,
765: [FILE IDS] INT ,
766: [PAGE IDS] INT ,
767: [Slot ID] INT ,
 768:           [AllocUnitId] BIGINT ,
		
		769: [RowLog Contents 0_var] VARCHAR(MAX) ,
770: [RowLog Length] VARCHAR(50) ,
771: [RowLog Len] INT ,
772: [RowLog Contents 0] VARBINARY(MAX) ,
773: [LINK ID] INT DEFAULT ( 0 ) ,
774: [Update] INT
 775:         )
		
		
 776:  
		
		777: DECLARE Page_Data_Cursor CURSOR
778: FOR
779: /*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*/
780: SELECT LTRIM(RTRIM(REPLACE([Description], 'Deallocated', ''))) AS [PAGE ID] ,
 781:                     [Slot ID] ,
		
		
 782:                     [AllocUnitId] ,
		
		783: NULL AS [RowLog Contents 0] ,
784: NULL AS [RowLog Contents 0] ,
 785:                     Context
		
		786: FROM sys.fn_dblog(NULL, NULL)
787: WHERE AllocUnitId IN (
788: SELECT [Allocation_unit_id]
789: FROM sys.allocation_units allocunits
790: INNER JOIN sys.partitions partitions ON ( allocunits.type IN (
 791:                                                               1, 3 )
		
		792: AND partitions.hobt_id = allocunits.container_id
 793:                                                               )
		
		794: OR ( allocunits.type = 2
795: AND partitions.partition_id = allocunits.container_id
 796:                                                               )
		
		797: WHERE object_id = OBJECT_ID('' + @SchemaName_n_TableName
798: + '') )
799: AND Operation IN ( 'LOP_MODIFY_ROW' )
800: AND [Context] IN ( 'LCX_PFS' )
801: AND Description LIKE '%Deallocated%'
802: /*Use this subquery to filter the date*/
803: AND [TRANSACTION ID] IN (
804: SELECT DISTINCT
805: [TRANSACTION ID]
806: FROM sys.fn_dblog(NULL, NULL)
807: WHERE Context IN ( 'LCX_NULL' )
808: AND Operation IN ( 'LOP_BEGIN_XACT' )
809: AND [Transaction Name] IN ('DELETE', 'user_transaction')
810: AND CONVERT(NVARCHAR(11), [Begin Time]) BETWEEN @Date_From
811: AND
 812:                                                               @Date_To )
		
		813: GROUP BY [Description] ,
 814:                     [Slot ID] ,
		
		
 815:                     [AllocUnitId] ,
		
		
 816:                     Context
		
		817: UNION
818: SELECT [PAGE ID] ,
 819:                     [Slot ID] ,
		
		
 820:                     [AllocUnitId] ,
		
		821: SUBSTRING([RowLog Contents 0], 15,
822: LEN([RowLog Contents 0])) AS [RowLog Contents 0] ,
823: CONVERT(INT, SUBSTRING([RowLog Contents 0], 7, 2)) ,
824: Context --,CAST(RIGHT([Current LSN],4) AS INT) AS [Current LSN]
825: FROM sys.fn_dblog(NULL, NULL)
826: WHERE AllocUnitId IN (
827: SELECT [Allocation_unit_id]
828: FROM sys.allocation_units allocunits
829: INNER JOIN sys.partitions partitions ON ( allocunits.type IN (
 830:                                                               1, 3 )
		
		831: AND partitions.hobt_id = allocunits.container_id
 832:                                                               )
		
		833: OR ( allocunits.type = 2
834: AND partitions.partition_id = allocunits.container_id
 835:                                                               )
		
		836: WHERE object_id = OBJECT_ID('' + @SchemaName_n_TableName
837: + '') )
838: AND Context IN ( 'LCX_TEXT_MIX' )
839: AND Operation IN ( 'LOP_DELETE_ROWS' )
840: /*Use this subquery to filter the date*/
841: AND [TRANSACTION ID] IN (
842: SELECT DISTINCT
843: [TRANSACTION ID]
844: FROM sys.fn_dblog(NULL, NULL)
845: WHERE Context IN ( 'LCX_NULL' )
846: AND Operation IN ( 'LOP_BEGIN_XACT' )
847: AND [Transaction Name] IN ('DELETE', 'user_transaction')
848: AND CONVERT(NVARCHAR(11), [Begin Time]) BETWEEN @Date_From
849: AND
 850:                                                               @Date_To )
		
		
 851:                          
		
		852: /****************************************/
 853:  
		
		854: OPEN Page_Data_Cursor
 855:  
		
		856: FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,
 857:         @AllocUnitID, @LCX_TEXT_MIX, @LinkID, @Context
		
		
 858:  
		
		859: WHILE @@FETCH_STATUS = 0
860: BEGIN
861: DECLARE @hex_pageid AS VARCHAR(MAX)
862: /*Page ID contains File Number and page number It looks like 0001:00000130.
863: In this example 0001 is file Number & 00000130 is Page Number & These numbers are in Hex format*/
864: SET @Fileid = SUBSTRING(@ConsolidatedPageID, 0,
865: CHARINDEX(':', @ConsolidatedPageID)) -- Seperate File ID from Page ID
 866:          
		
		867: SET @hex_pageid = '0x' + SUBSTRING(@ConsolidatedPageID,
868: CHARINDEX(':',
 869:                                                          @ConsolidatedPageID)
		
		
 870:                                                + 1, LEN(@ConsolidatedPageID))  ---Seperate the page ID
		
		871: SELECT @Pageid = CONVERT(INT, CAST('' AS XML).value('xs:hexBinary(substring(sql:variable("@hex_pageid"),sql:column("t.pos")) )',
872: 'varbinary(max)')) -- Convert Page ID from hex to integer
873: FROM ( SELECT CASE SUBSTRING(@hex_pageid, 1, 2)
874: WHEN '0x' THEN 3
875: ELSE 0
876: END
877: ) AS t ( pos )
 878:              
		
		879: IF @Context = 'LCX_PFS'
880: BEGIN
881: DELETE @temppagedata
882: INSERT INTO @temppagedata
883: EXEC
884: ( 'DBCC PAGE(' + @Database_Name + ', '
885: + @Fileid + ', ' + @Pageid
886: + ', 1) with tableresults,no_infomsgs;'
 887:                                 ); 
		
		888: INSERT INTO @pagedata
889: SELECT @ConsolidatedPageID ,
 890:                                     @Fileid ,
		
		
 891:                                     @Pageid ,
		
		
 892:                                     @AllocUnitID ,
		
		
 893:                                     [ParentObject] ,
		
		894: [Object] ,
 895:                                     [Field] ,
		
		896: [Value]
897: FROM @temppagedata
898: END
899: ELSE
900: IF @Context = 'LCX_TEXT_MIX'
901: BEGIN
902: INSERT INTO @ModifiedRawData
903: SELECT @ConsolidatedPageID ,
 904:                                         @Fileid ,
		
		
 905:                                         @Pageid ,
		
		
 906:                                         @Slotid ,
		
		
 907:                                         @AllocUnitID ,
		
		908: NULL ,
 909:                                         0 ,
		
		910: CONVERT(INT, CONVERT(VARBINARY, REVERSE(SUBSTRING(@LCX_TEXT_MIX,
 911:                                                               11, 2)))) ,
		
		
 912:                                         @LCX_TEXT_MIX ,
		
		
 913:                                         @LinkID ,
		
		
 914:                                         0
		
		915: END
916: FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,
 917:                 @AllocUnitID, @LCX_TEXT_MIX, @LinkID, @Context
		
		918: END
 919:      
		
		920: CLOSE Page_Data_Cursor
921: DEALLOCATE Page_Data_Cursor
 922:  
		
		923: DECLARE @Newhexstring VARCHAR(MAX);
 924:  
		
		925: --The data is in multiple rows in the page, so we need to convert it into one row as a single hex value.
926: --This hex value is in string format
927: INSERT INTO @ModifiedRawData
 928:             ( [PAGE ID] ,
		
		929: [FILE IDS] ,
 930:               [PAGE IDS] ,
		
		
 931:               [Slot ID] ,
		
		
 932:               [AllocUnitId] ,
		
		
 933:               [RowLog Contents 0_var] ,
		
		
 934:               [RowLog Length]
		
		
 935:             )
		
		936: SELECT [Page ID] ,
937: [FILE IDS] ,
 938:                     [PAGE IDS] ,
		
		939: SUBSTRING([ParentObject],
940: CHARINDEX('Slot', [ParentObject]) + 4,
941: ( CHARINDEX('Offset', [ParentObject])
942: - ( CHARINDEX('Slot', [ParentObject]) + 4 ) )
943: - 2) AS [Slot ID] ,
 944:                     [AllocUnitId] ,
		
		945: SUBSTRING(( SELECT REPLACE(STUFF(( SELECT
946: REPLACE(SUBSTRING([Value],
947: CHARINDEX(':',
948: [Value]) + 1,
949: CHARINDEX('†',
950: [Value])
951: - CHARINDEX(':',
952: [Value])), '†',
953: '')
954: FROM @pagedata C
955: WHERE B.[Page ID] = C.[Page ID]
956: AND SUBSTRING(B.[ParentObject],
957: CHARINDEX('Slot',
 958:                                                               B.[ParentObject])
		
		
 959:                                                               + 4,
		
		960: ( CHARINDEX('Offset',
 961:                                                               B.[ParentObject])
		
		962: - ( CHARINDEX('Slot',
 963:                                                               B.[ParentObject])
		
		964: + 4 ) )) = SUBSTRING(C.[ParentObject],
965: CHARINDEX('Slot',
 966:                                                               C.[ParentObject])
		
		
 967:                                                               + 4,
		
		968: ( CHARINDEX('Offset',
 969:                                                               C.[ParentObject])
		
		970: - ( CHARINDEX('Slot',
 971:                                                               C.[ParentObject])
		
		
 972:                                                               + 4 ) ))
		
		973: AND [Object] LIKE '%Memory Dump%'
974: ORDER BY '0x'
975: + LEFT([Value],
976: CHARINDEX(':',
977: [Value]) - 1)
978: FOR
979: XML PATH('')
980: ), 1, 1, ''), ' ', '')
981: ), 1, 20000) AS [Value] ,
982: SUBSTRING(( SELECT '0x'
983: + REPLACE(STUFF(( SELECT
984: REPLACE(SUBSTRING([Value],
985: CHARINDEX(':',
986: [Value]) + 1,
987: CHARINDEX('†',
988: [Value])
989: - CHARINDEX(':',
990: [Value])), '†',
991: '')
992: FROM
 993:                                                               @pagedata C
		
		994: WHERE
 995:                                                               B.[Page ID] = C.[Page ID]
		
		996: AND SUBSTRING(B.[ParentObject],
997: CHARINDEX('Slot',
 998:                                                               B.[ParentObject])
		
		
 999:                                                               + 4,
		
		1000: ( CHARINDEX('Offset',
1001:                                                               B.[ParentObject])
		
		1002: - ( CHARINDEX('Slot',
1003:                                                               B.[ParentObject])
		
		1004: + 4 ) )) = SUBSTRING(C.[ParentObject],
1005: CHARINDEX('Slot',
1006:                                                               C.[ParentObject])
		
		
1007:                                                               + 4,
		
		1008: ( CHARINDEX('Offset',
1009:                                                               C.[ParentObject])
		
		1010: - ( CHARINDEX('Slot',
1011:                                                               C.[ParentObject])
		
		
1012:                                                               + 4 ) ))
		
		1013: AND [Object] LIKE '%Memory Dump%'
1014: ORDER BY '0x'
1015: + LEFT([Value],
1016: CHARINDEX(':',
1017: [Value]) - 1)
1018: FOR
1019: XML PATH('')
1020: ), 1, 1, ''), ' ', '')
1021: ), 7, 4) AS [Length]
1022: FROM @pagedata B
1023: WHERE [Object] LIKE '%Memory Dump%'
1024: GROUP BY [Page ID] ,
1025: [FILE IDS] ,
1026:                     [PAGE IDS] ,
		
		
1027:                     [ParentObject] ,
		
		1028: [AllocUnitId]--,[Current LSN]
1029: ORDER BY [Slot ID]
1030:  
		
		1031: UPDATE @ModifiedRawData
1032: SET [RowLog Len] = CONVERT(VARBINARY(8000), REVERSE(CAST('' AS XML).value('xs:hexBinary(substring(sql:column("[RowLog Length]"),0))',
1033: 'varbinary(Max)')))
1034: FROM @ModifiedRawData
1035: WHERE [LINK ID] = 0
1036:  
		
		1037: UPDATE @ModifiedRawData
1038: SET [RowLog Contents 0] = CAST('' AS XML).value('xs:hexBinary(substring(sql:column("[RowLog Contents 0_var]"),0))',
1039: 'varbinary(Max)')
1040: FROM @ModifiedRawData
1041: WHERE [LINK ID] = 0
1042:  
		
		1043: UPDATE B
1044: SET B.[RowLog Contents 0] = ( CASE WHEN A.[RowLog Contents 0] IS NOT NULL
1045: AND C.[RowLog Contents 0] IS NOT NULL
1046: THEN A.[RowLog Contents 0]
1047:                                                 + C.[RowLog Contents 0]
		
		1048: WHEN A.[RowLog Contents 0] IS NULL
1049: AND C.[RowLog Contents 0] IS NOT NULL
1050: THEN C.[RowLog Contents 0]
1051: WHEN A.[RowLog Contents 0] IS NOT NULL
1052: AND C.[RowLog Contents 0] IS NULL
1053: THEN A.[RowLog Contents 0]
1054: END ) ,
1055: B.[Update] = ISNULL(B.[Update], 0) + 1
1056: FROM @ModifiedRawData B
1057: LEFT JOIN @ModifiedRawData A ON A.[PAGE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1058:                                                               15 + 14, 2))))
		
		1059: AND A.[FILE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1060:                                                               19 + 14, 2))))
		
		1061: AND A.[LINK ID] = B.[LINK ID]
1062: LEFT JOIN @ModifiedRawData C ON C.[PAGE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1063:                                                               27 + 14, 2))))
		
		1064: AND C.[FILE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1065:                                                               31 + 14, 2))))
		
		1066: AND C.[LINK ID] = B.[LINK ID]
1067: WHERE ( A.[RowLog Contents 0] IS NOT NULL
1068: OR C.[RowLog Contents 0] IS NOT NULL
1069:             )
		
		
1070:  
		
		
1071:  
		
		1072: UPDATE B
1073: SET B.[RowLog Contents 0] = ( CASE WHEN A.[RowLog Contents 0] IS NOT NULL
1074: AND C.[RowLog Contents 0] IS NOT NULL
1075: THEN A.[RowLog Contents 0]
1076:                                                 + C.[RowLog Contents 0]
		
		1077: WHEN A.[RowLog Contents 0] IS NULL
1078: AND C.[RowLog Contents 0] IS NOT NULL
1079: THEN C.[RowLog Contents 0]
1080: WHEN A.[RowLog Contents 0] IS NOT NULL
1081: AND C.[RowLog Contents 0] IS NULL
1082: THEN A.[RowLog Contents 0]
1083: END )
1084: --,B.[Update]=ISNULL(B.[Update],0)+1
1085: FROM @ModifiedRawData B
1086: LEFT JOIN @ModifiedRawData A ON A.[PAGE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1087:                                                               15 + 14, 2))))
		
		1088: AND A.[FILE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1089:                                                               19 + 14, 2))))
		
		1090: AND A.[LINK ID] <> B.[LINK ID]
1091: AND B.[Update] = 0
1092: LEFT JOIN @ModifiedRawData C ON C.[PAGE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1093:                                                               27 + 14, 2))))
		
		1094: AND C.[FILE IDS] = CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING(B.[RowLog Contents 0],
1095:                                                               31 + 14, 2))))
		
		1096: AND C.[LINK ID] <> B.[LINK ID]
1097: AND B.[Update] = 0
1098: WHERE ( A.[RowLog Contents 0] IS NOT NULL
1099: OR C.[RowLog Contents 0] IS NOT NULL
1100:             )
		
		
1101:  
		
		1102: UPDATE @ModifiedRawData
1103: SET [RowLog Contents 0] = ( CASE WHEN [RowLog Len] >= 8000
1104: THEN SUBSTRING([RowLog Contents 0],
1105:                                                         15, [RowLog Len])
		
		1106: WHEN [RowLog Len] < 8000
1107: THEN SUBSTRING([RowLog Contents 0],
1108:                                                         15 + 6,
		
		1109: CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING([RowLog Contents 0],
1110:                                                               15, 6)))))
		
		1111: END )
1112: FROM @ModifiedRawData
1113: WHERE [LINK ID] = 0
1114:  
		
		1115: UPDATE @ColumnNameAndData
1116: SET [hex_Value] = [RowLog Contents 0]
1117: --,A.[Update]=A.[Update]+1
1118: FROM @ColumnNameAndData A
1119: INNER JOIN @ModifiedRawData B ON CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING([hex_Value],
1120:                                                               17, 4)))) = [PAGE IDS]
		
		1121: AND CONVERT(INT, SUBSTRING([hex_Value],
1122:                                                               9, 2)) = B.[LINK ID]
		
		1123: WHERE [system_type_id] IN ( 99, 167, 175, 231, 239, 241, 165, 98 )
1124: AND [LINK ID] <> 0
1125:  
		
		1126: UPDATE @ColumnNameAndData
1127: SET [hex_Value] = ( CASE WHEN B.[RowLog Contents 0] IS NOT NULL
1128: AND C.[RowLog Contents 0] IS NOT NULL
1129: THEN B.[RowLog Contents 0]
1130:                                       + C.[RowLog Contents 0]
		
		1131: WHEN B.[RowLog Contents 0] IS NULL
1132: AND C.[RowLog Contents 0] IS NOT NULL
1133: THEN C.[RowLog Contents 0]
1134: WHEN B.[RowLog Contents 0] IS NOT NULL
1135: AND C.[RowLog Contents 0] IS NULL
1136: THEN B.[RowLog Contents 0]
1137: END )
1138: --,A.[Update]=A.[Update]+1
1139: FROM @ColumnNameAndData A
1140: LEFT JOIN @ModifiedRawData B ON CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING([hex_Value],
1141:                                                               5, 4)))) = B.[PAGE IDS]
		
		1142: AND B.[LINK ID] = 0
1143: LEFT JOIN @ModifiedRawData C ON CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING([hex_Value],
1144:                                                               17, 4)))) = C.[PAGE IDS]
		
		1145: AND C.[LINK ID] = 0
1146: WHERE [system_type_id] IN ( 99, 167, 175, 231, 239, 241, 165, 98 )
1147: AND ( B.[RowLog Contents 0] IS NOT NULL
1148: OR C.[RowLog Contents 0] IS NOT NULL
1149:                 )
		
		
1150:  
		
		1151: UPDATE @ColumnNameAndData
1152: SET [hex_Value] = [RowLog Contents 0]
1153: --,A.[Update]=A.[Update]+1
1154: FROM @ColumnNameAndData A
1155: INNER JOIN @ModifiedRawData B ON CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING([hex_Value],
1156:                                                               9, 4)))) = [PAGE IDS]
		
		1157: AND CONVERT(INT, SUBSTRING([hex_Value],
1158:                                                               3, 2)) = [LINK ID]
		
		1159: WHERE [system_type_id] IN ( 35, 34, 99 )
1160: AND [LINK ID] <> 0
1161:      
		
		1162: UPDATE @ColumnNameAndData
1163: SET [hex_Value] = [RowLog Contents 0]
1164: --,A.[Update]=A.[Update]+10
1165: FROM @ColumnNameAndData A
1166: INNER JOIN @ModifiedRawData B ON CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING([hex_Value],
1167:                                                               9, 4)))) = [PAGE IDS]
		
		1168: WHERE [system_type_id] IN ( 35, 34, 99 )
1169: AND [LINK ID] = 0
1170:  
		
		1171: UPDATE @ColumnNameAndData
1172: SET [hex_Value] = [RowLog Contents 0]
1173: --,A.[Update]=A.[Update]+1
1174: FROM @ColumnNameAndData A
1175: INNER JOIN @ModifiedRawData B ON CONVERT(INT, CONVERT(VARBINARY(MAX), REVERSE(SUBSTRING([hex_Value],
1176:                                                               15, 4)))) = [PAGE IDS]
		
		1177: WHERE [system_type_id] IN ( 35, 34, 99 )
1178: AND [LINK ID] = 0
1179:  
		
		1180: UPDATE @ColumnNameAndData
1181: SET [hex_Value] = 0xFFFE + SUBSTRING([hex_Value], 9, LEN([hex_Value]))
1182: --,[Update]=[Update]+1
1183: WHERE [system_type_id] = 241
1184:  
		
		1185: CREATE TABLE [#temp_Data]
1186:         (
		
		1187: [FieldName] VARCHAR(MAX) ,
1188: [FieldValue] NVARCHAR(MAX) ,
1189:           [RowLogContents] VARBINARY(8000) ,
		
		1190: [Row ID] INT ,
1191: [Transaction ID] VARCHAR(100) ,
1192: [Deletion Date Time] DATETIME ,
1193: [Deleted By User Name] VARCHAR(MAX)
1194:         )
		
		
1195:  
		
		1196: INSERT INTO #temp_Data
1197: SELECT NAME ,
1198: CASE WHEN system_type_id IN ( 231, 239 )
1199: THEN LTRIM(RTRIM(CONVERT(NVARCHAR(MAX), hex_Value))) --NVARCHAR ,NCHAR
1200: WHEN system_type_id IN ( 167, 175 )
1201: THEN LTRIM(RTRIM(CONVERT(VARCHAR(MAX), hex_Value))) --VARCHAR,CHAR
1202: WHEN system_type_id IN ( 35 )
1203: THEN LTRIM(RTRIM(CONVERT(VARCHAR(MAX), hex_Value))) --Text
1204: WHEN system_type_id IN ( 99 )
1205: THEN LTRIM(RTRIM(CONVERT(NVARCHAR(MAX), hex_Value))) --nText
1206: WHEN system_type_id = 48
1207: THEN CONVERT(VARCHAR(MAX), CONVERT(TINYINT, CONVERT(BINARY(1), REVERSE(hex_Value)))) --TINY INTEGER
1208: WHEN system_type_id = 52
1209: THEN CONVERT(VARCHAR(MAX), CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(hex_Value)))) --SMALL INTEGER
1210: WHEN system_type_id = 56
1211: THEN CONVERT(VARCHAR(MAX), CONVERT(INT, CONVERT(BINARY(4), REVERSE(hex_Value)))) -- INTEGER
1212: WHEN system_type_id = 127
1213: THEN CONVERT(VARCHAR(MAX), CONVERT(BIGINT, CONVERT(BINARY(8), REVERSE(hex_Value))))-- BIG INTEGER
1214: WHEN system_type_id = 61
1215: THEN CONVERT(VARCHAR(MAX), CONVERT(DATETIME, CONVERT(VARBINARY(8000), REVERSE(hex_Value))), 100) --DATETIME
1216: WHEN system_type_id = 58
1217: THEN CONVERT(VARCHAR(MAX), CONVERT(SMALLDATETIME, CONVERT(VARBINARY(8000), REVERSE(hex_Value))), 100) --SMALL DATETIME
1218: WHEN system_type_id = 108
1219: THEN CONVERT(VARCHAR(MAX), CONVERT(NUMERIC(38, 20), CONVERT(VARBINARY, CONVERT(VARBINARY(1), xprec)
1220: + CONVERT(VARBINARY(1), xscale))
1221: + CONVERT(VARBINARY(1), 0) + hex_Value)) --- NUMERIC
1222: WHEN system_type_id = 106
1223: THEN CONVERT(VARCHAR(MAX), CONVERT(DECIMAL(38, 20), CONVERT(VARBINARY, CONVERT(VARBINARY(1), xprec)
1224: + CONVERT(VARBINARY(1), xscale))
1225: + CONVERT(VARBINARY(1), 0) + hex_Value)) --- DECIMAL
1226: WHEN system_type_id IN ( 60, 122 )
1227: THEN CONVERT(VARCHAR(MAX), CONVERT(MONEY, CONVERT(VARBINARY(8000), REVERSE(hex_Value))), 2) --MONEY,SMALLMONEY
1228: WHEN system_type_id = 104
1229: THEN CONVERT(VARCHAR(MAX), CONVERT (BIT, CONVERT(BINARY(1), hex_Value)
1230: % 2)) -- BIT
1231: WHEN system_type_id = 62
1232: THEN RTRIM(LTRIM(STR(CONVERT(FLOAT, SIGN(CAST(CONVERT(VARBINARY(8000), REVERSE(hex_Value)) AS BIGINT))
1233:                                               * ( 1.0
		
		1234: + ( CAST(CONVERT(VARBINARY(8000), REVERSE(hex_Value)) AS BIGINT)
1235:                                                       & 0x000FFFFFFFFFFFFF )
		
		1236: * POWER(CAST(2 AS FLOAT),
1237:                                                           -52) )
		
		1238: * POWER(CAST(2 AS FLOAT),
1239: ( ( CAST(CONVERT(VARBINARY(8000), REVERSE(hex_Value)) AS BIGINT)
1240:                                                           & 0x7ff0000000000000 )
		
		
1241:                                                         / EXP(52 * LOG(2))
		
		
1242:                                                         - 1023 ))), 53,
		
		1243: LEN(hex_Value)))) --- FLOAT
1244: WHEN system_type_id = 59
1245: THEN LEFT(LTRIM(STR(CAST(SIGN(CAST(CONVERT(VARBINARY(8000), REVERSE(hex_Value)) AS BIGINT))
1246:                                              * ( 1.0
		
		1247: + ( CAST(CONVERT(VARBINARY(8000), REVERSE(hex_Value)) AS BIGINT)
1248:                                                      & 0x007FFFFF )
		
		1249: * POWER(CAST(2 AS REAL), -23) )
1250: * POWER(CAST(2 AS REAL),
1251: ( ( ( CAST(CONVERT(VARBINARY(8000), REVERSE(hex_Value)) AS INT) )
1252:                                                          & 0x7f800000 )
		
		
1253:                                                        / EXP(23 * LOG(2))
		
		1254: - 127 )) AS REAL), 23,
1255: 23)), 8) --Real
1256: WHEN system_type_id IN ( 165, 173 )
1257: THEN ( CASE WHEN CHARINDEX(0x,
1258: CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1259: 'VARBINARY(8000)')) = 0
1260: THEN '0x'
1261: ELSE ''
1262: END ) + CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1263: 'varchar(max)') -- BINARY,VARBINARY
1264: WHEN system_type_id = 34
1265: THEN ( CASE WHEN CHARINDEX(0x,
1266: CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1267: 'VARBINARY(8000)')) = 0
1268: THEN '0x'
1269: ELSE ''
1270: END ) + CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1271: 'varchar(max)') --IMAGE
1272: WHEN system_type_id = 36
1273: THEN CONVERT(VARCHAR(MAX), CONVERT(UNIQUEIDENTIFIER, hex_Value)) --UNIQUEIDENTIFIER
1274: WHEN system_type_id = 231
1275: THEN CONVERT(VARCHAR(MAX), CONVERT(nvarchar(128), hex_Value)) --nvarchar(128)
1276: WHEN system_type_id = 241
1277: THEN CONVERT(VARCHAR(MAX), CONVERT(XML, hex_Value)) --XML
1278: WHEN system_type_id = 189
1279: THEN ( CASE WHEN CHARINDEX(0x,
1280: CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1281: 'VARBINARY(8000)')) = 0
1282: THEN '0x'
1283: ELSE ''
1284: END ) + CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1285: 'varchar(max)') --TIMESTAMP
1286: WHEN system_type_id = 98
1287: THEN ( CASE WHEN CONVERT(INT, SUBSTRING(hex_Value, 1,
1288:                                                               1)) = 56
		
		1289: THEN CONVERT(VARCHAR(MAX), CONVERT(INT, CONVERT(BINARY(4), REVERSE(SUBSTRING(hex_Value,
1290:                                                               3,
		
		1291: LEN(hex_Value)))))) -- INTEGER
1292: WHEN CONVERT(INT, SUBSTRING(hex_Value, 1,
1293:                                                               1)) = 108
		
		1294: THEN CONVERT(VARCHAR(MAX), CONVERT(NUMERIC(38,
1295: 20), CONVERT(VARBINARY(1), SUBSTRING(hex_Value,
1296:                                                               3, 1))
		
		1297: + CONVERT(VARBINARY(1), SUBSTRING(hex_Value,
1298:                                                               4, 1))
		
		1299: + CONVERT(VARBINARY(1), 0)
1300: + SUBSTRING(hex_Value, 5,
1301: LEN(hex_Value)))) --- NUMERIC
1302: WHEN CONVERT(INT, SUBSTRING(hex_Value, 1,
1303:                                                               1)) = 167
		
		1304: THEN LTRIM(RTRIM(CONVERT(VARCHAR(MAX), SUBSTRING(hex_Value,
1305:                                                               9,
		
		1306: LEN(hex_Value))))) --VARCHAR,CHAR
1307: WHEN CONVERT(INT, SUBSTRING(hex_Value, 1,
1308:                                                               1)) = 36
		
		1309: THEN CONVERT(VARCHAR(MAX), CONVERT(UNIQUEIDENTIFIER, SUBSTRING(( hex_Value ),
1310:                                                               3, 20))) --UNIQUEIDENTIFIER
		
		1311: WHEN CONVERT(INT, SUBSTRING(hex_Value, 1,
1312:                                                               1)) = 61
		
		1313: THEN CONVERT(VARCHAR(MAX), CONVERT(DATETIME, CONVERT(VARBINARY(8000), REVERSE(SUBSTRING(hex_Value,
1314:                                                               3,
		
		
1315:                                                               LEN(hex_Value))))), 100) --DATETIME
		
		1316: WHEN CONVERT(INT, SUBSTRING(hex_Value, 1,
1317:                                                               1)) = 165
		
		1318: THEN '0x'
1319: + SUBSTRING(( CASE WHEN CHARINDEX(0x,
1320: CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1321: 'VARBINARY(8000)')) = 0
1322: THEN '0x'
1323: ELSE ''
1324: END )
1325: + CAST('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))',
1326: 'varchar(max)'),
1327: 11, LEN(hex_Value)) -- BINARY,VARBINARY
1328: END )
1329: END AS FieldValue ,
1330:                     [RowLogContents] ,
		
		1331: [Row ID] ,
1332: [Transaction ID] ,
1333: NULL ,
1334: NULL
1335: FROM @ColumnNameAndData
1336: ORDER BY nullbit
1337:  
		
		1338: --Find the user ID and date time
1339: UPDATE #temp_Data
1340: SET [Deleted By User Name] = SUSER_SNAME(B.[Transaction SID]),
1341: [Deletion Date Time] = [Begin Time]
1342: FROM #temp_Data A
1343: INNER JOIN fn_dblog(NULL, NULL) B ON A.[Transaction ID] = B.[Transaction ID]
1344: WHERE B.[Operation] = 'LOP_BEGIN_XACT'
1345: AND B.[Context] = 'LCX_NULL'
1346: AND B.[Transaction Name] IN ('DELETE', 'user_transaction')
1347:  
		
		1348: --Create the column name in the same order to do pivot table.
1349:  
		
		1350: DECLARE @FieldName VARCHAR(MAX)
1351: DECLARE @AdditionalField VARCHAR(MAX)
1352: SET @FieldName = STUFF(( SELECT ','
1353: + CAST(QUOTENAME([Name]) AS VARCHAR(MAX))
1354: FROM syscolumns
1355: WHERE id = OBJECT_ID(''
1356:                                                    + @SchemaName_n_TableName
		
		1357: + '')
1358: FOR
1359: XML PATH('')
1360: ), 1, 1, '')
1361:  
		
		1362: --Finally did pivot table and get the data back in the same format.
1363:  
		
		1364: SET @AdditionalField = @FieldName
1365: + ' ,[Deleted By User Name],[Deletion Date Time]'
1366:  
		
		1367: SET @SQL = 'SELECT ' + @AdditionalField
1368: + ' FROM #temp_Data PIVOT (Min([FieldValue]) FOR FieldName IN ('
1369: + @FieldName + ')) AS pvt'
1370: PRINT @SQL
1371: EXEC sp_executesql @SQL
1372:  
		
		
1373: GO
		
對這個問題有興趣的朋友,可以參考亂馬客的文章 [SQL]透過 Transaction Log(fn_dblog) 取回被刪除的資料。