【sqlserver】在没有数据库备份的情况下,获得操作记录信息【code】
原文链接:How to recover modified records from SQL Server without Backup
SQL Server 2005或以上恢复修改的数据。(注:这个脚本可以恢复以下数据类型与CS整理兼容)。
- tinyint
- smallint
- int
- bigint
- bit
- char
- varchar
- nchar
- nvarchar
- datetime
- smalldatetime
- money
- smallmoney
- decimal
- numeric
- real
- float
- binary
- varbinary
- uniqueidentifier
让我解释这个问题演示简单的例子。
--Create Table CREATE TABLE [dbo].[Student]( [Sno] [int] NOT NULL, [Student ID] nvarchar(6) Not NULL , [Student name] [varchar](50) NOT NULL, [Date of Birth] datetime not null, [Weight] [int] NULL) --Inserting data into table Insert into dbo.[Student] values (1,'STD001','Bob','2003-12-31',40) Insert into dbo.[Student] values (2,'STD002','Alexander','2004-11-15',35) --Check the existence of the data Select * from dbo.[Student]
Update
[Student]
Set
[Student
Name
]=
'Bob jerry'
--Where [SNO]=1 forget to use where clause
Select
*
from
dbo.[Student]
1 Create PROCEDURE Recover_Modified_Data_Proc 2 @Database_Name NVARCHAR(MAX), 3 @SchemaName_n_TableName NVARCHAR(MAX), 4 @Date_From datetime='1900/01/01', 5 @Date_To datetime ='9999/12/31' 6 AS 7 DECLARE @parms nvarchar(1024) 8 DECLARE @Fileid INT 9 DECLARE @Pageid INT 10 DECLARE @Slotid INT 11 DECLARE @RowLogContents0 VARBINARY(8000) 12 DECLARE @RowLogContents1 VARBINARY(8000) 13 DECLARE @RowLogContents3 VARBINARY(8000) 14 DECLARE @RowLogContents3_Var VARCHAR(MAX) 15 16 DECLARE @RowLogContents4 VARBINARY(8000) 17 DECLARE @LogRecord VARBINARY(8000) 18 DECLARE @LogRecord_Var VARCHAR(MAX) 19 20 DECLARE @ConsolidatedPageID VARCHAR(MAX) 21 Declare @AllocUnitID as bigint 22 Declare @TransactionID as VARCHAR(MAX) 23 Declare @Operation as VARCHAR(MAX) 24 Declare @DatabaseCollation VARCHAR(MAX) 25 26 27 /* Pick The actual data 28 */ 29 declare @temppagedata table 30 ( 31 [ParentObject] sysname, 32 [Object] sysname, 33 [Field] sysname, 34 [Value] sysname) 35 36 declare @pagedata table 37 ( 38 [Page ID] sysname, 39 [AllocUnitId] bigint, 40 [ParentObject] sysname, 41 [Object] sysname, 42 [Field] sysname, 43 [Value] sysname) 44 45 46 DECLARE Page_Data_Cursor CURSOR FOR 47 /*We need to filter LOP_MODIFY_ROW,LOP_MODIFY_COLUMNS from log for modified records & Get its Slot No, Page ID & AllocUnit ID*/ 48 SELECT [PAGE ID],[Slot ID],[AllocUnitId] 49 FROM sys.fn_dblog(NULL, NULL) 50 WHERE 51 AllocUnitId IN 52 (Select [Allocation_unit_id] from sys.allocation_units allocunits 53 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3) 54 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 55 AND partitions.partition_id = allocunits.container_id) 56 Where object_id=object_ID('' + @SchemaName_n_TableName + '')) 57 AND Operation IN ('LOP_MODIFY_ROW','LOP_MODIFY_COLUMNS') AND [Context] IN ('LCX_HEAP','LCX_CLUSTERED') 58 /*Use this subquery to filter the date*/ 59 60 AND [TRANSACTION ID] IN (SELECT DISTINCT [TRANSACTION ID] FROM sys.fn_dblog(NULL, NULL) 61 WHERE Context IN ('LCX_NULL') AND Operation in ('LOP_BEGIN_XACT') 62 AND [Transaction Name]='UPDATE' 63 AND CONVERT(NVARCHAR(11),[Begin Time]) BETWEEN @Date_From AND @Date_To) 64 65 /****************************************/ 66 67 GROUP BY [PAGE ID],[Slot ID],[AllocUnitId] 68 ORDER BY [Slot ID] 69 70 OPEN Page_Data_Cursor 71 72 FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,@AllocUnitID 73 74 WHILE @@FETCH_STATUS = 0 75 BEGIN 76 DECLARE @hex_pageid AS VARCHAR(Max) 77 /*Page ID contains File Number and page number It looks like 0001:00000130. 78 In this example 0001 is file Number & 00000130 is Page Number & These numbers are in Hex format*/ 79 SET @Fileid=SUBSTRING(@ConsolidatedPageID,0,CHARINDEX(':',@ConsolidatedPageID)) -- Seperate File ID from Page ID 80 SET @hex_pageid ='0x'+ SUBSTRING(@ConsolidatedPageID,CHARINDEX(':',@ConsolidatedPageID)+1,Len(@ConsolidatedPageID)) ---Seperate the page ID 81 82 SELECT @Pageid=Convert(INT,cast('' AS XML).value('xs:hexBinary(substring(sql:variable("@hex_pageid"),sql:column("t.pos")) )', 'varbinary(max)')) -- Convert Page ID from hex to integer 83 FROM (SELECT CASE substring(@hex_pageid, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos) 84 85 DELETE @temppagedata 86 -- Now we need to get the actual data (After modification) from the page 87 INSERT INTO @temppagedata EXEC( 'DBCC PAGE(' + @DataBase_Name + ', ' + @fileid + ', ' + @pageid + ', 3) with tableresults,no_infomsgs;'); 88 -- Add Page Number and allocUnit ID in data to identity which one page it belongs to. 89 INSERT INTO @pagedata SELECT @ConsolidatedPageID,@AllocUnitID,[ParentObject],[Object],[Field] ,[Value] FROM @temppagedata 90 91 FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,@AllocUnitID 92 END 93 94 CLOSE Page_Data_Cursor 95 DEALLOCATE Page_Data_Cursor 96 97 DECLARE @Newhexstring VARCHAR(MAX); 98 99 100 DECLARE @ModifiedRawData TABLE 101 ( 102 [ID] INT IDENTITY(1,1), 103 [PAGE ID] VARCHAR(MAX), 104 [Slot ID] INT, 105 [AllocUnitId] BIGINT, 106 [RowLog Contents 0_var] VARCHAR(MAX), 107 [RowLog Contents 0] VARBINARY(8000) 108 ) 109 --The modified data is in multiple rows in the page, so we need to convert it into one row as a single hex value. 110 --This hex value is in string format 111 INSERT INTO @ModifiedRawData ([PAGE ID],[Slot ID],[AllocUnitId] 112 ,[RowLog Contents 0_var]) 113 SELECT B.[PAGE ID],A.[Slot ID],A.[AllocUnitId] 114 ,( 115 SELECT REPLACE(STUFF((SELECT REPLACE(SUBSTRING([VALUE],CHARINDEX(':',[Value])+1,48),'†','') 116 FROM @pagedata C WHERE B.[Page ID]= C.[Page ID] And A.[Slot ID] =LTRIM(RTRIM(SUBSTRING(C.[ParentObject],5,3))) And [Object] Like '%Memory Dump%' 117 Group By [Value] FOR XML PATH('') ),1,1,'') ,' ','') 118 ) AS [Value] 119 120 FROM sys.fn_dblog(NULL, NULL) A 121 INNER JOIN @pagedata B On A.[PAGE ID]=B.[PAGE ID] 122 AND A.[AllocUnitId]=B.[AllocUnitId] 123 AND A.[Slot ID] =LTRIM(RTRIM(Substring(B.[ParentObject],5,3))) 124 AND B.[Object] Like '%Memory Dump%' 125 WHERE A.AllocUnitId IN 126 (Select [Allocation_unit_id] from sys.allocation_units allocunits 127 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3) 128 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 129 AND partitions.partition_id = allocunits.container_id) 130 Where object_id=object_ID('' + @SchemaName_n_TableName + '')) 131 AND Operation in ('LOP_MODIFY_ROW','LOP_MODIFY_COLUMNS') AND [Context] IN ('LCX_HEAP','LCX_CLUSTERED') 132 /*Use this subquery to filter the date*/ 133 134 AND [TRANSACTION ID] IN (Select DISTINCT [TRANSACTION ID] FROM sys.fn_dblog(NULL, NULL) 135 Where Context IN ('LCX_NULL') AND Operation IN ('LOP_BEGIN_XACT') 136 AND [Transaction Name]='UPDATE' 137 AND CONVERT(NVARCHAR(11),[Begin Time]) BETWEEN @Date_From AND @Date_To) 138 139 /****************************************/ 140 GROUP BY B.[PAGE ID],A.[Slot ID],A.[AllocUnitId]--,[Transaction ID] 141 ORDER BY [Slot ID] 142 143 144 -- Convert the hex value data in string, convert it into Hex value as well. 145 UPDATE @ModifiedRawData SET [RowLog Contents 0] = cast('' AS XML).value('xs:hexBinary(substring(sql:column("[RowLog Contents 0_var]"), 0) )', 'varbinary(max)') 146 FROM @ModifiedRawData 147 148 ---Now we have modifed data plus its slot ID , page ID and allocunit as well. 149 --After that we need to get the old values before modfication, these datas are in chunks. 150 DECLARE Page_Data_Cursor CURSOR FOR 151 152 Select [PAGE ID],[Slot ID],[AllocUnitId],[Transaction ID],[RowLog Contents 0], [RowLog Contents 1],[RowLog Contents 3],[RowLog Contents 4] 153 ,Substring ([Log Record],[Log Record Fixed Length],([Log Record Length]+1)-([Log Record Fixed Length])) as [Log Record] 154 ,Operation 155 FROM sys.fn_dblog(NULL, NULL) 156 WHERE AllocUnitId IN 157 (Select [Allocation_unit_id] from sys.allocation_units allocunits 158 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3) 159 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 160 AND partitions.partition_id = allocunits.container_id) 161 Where object_id=object_ID('' + @SchemaName_n_TableName + '')) 162 AND Operation in ('LOP_MODIFY_ROW','LOP_MODIFY_COLUMNS') And [Context] IN ('LCX_HEAP','LCX_CLUSTERED') 163 /*Use this sub query to filter the date*/ 164 165 AND [TRANSACTION ID] IN (Select DISTINCT [TRANSACTION ID] FROM sys.fn_dblog(NULL, NULL) 166 WHERE Context IN ('LCX_NULL') AND Operation IN ('LOP_BEGIN_XACT') 167 AND [Transaction Name]='UPDATE' 168 AND CONVERT(NVARCHAR(11),[Begin Time]) BETWEEN @Date_From AND @Date_To) 169 170 /****************************************/ 171 Order By [Slot ID],[Transaction ID] DESC 172 173 OPEN Page_Data_Cursor 174 175 FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,@AllocUnitID,@TransactionID,@RowLogContents0,@RowLogContents1,@RowLogContents3,@RowLogContents4,@LogRecord,@Operation 176 WHILE @@FETCH_STATUS = 0 177 BEGIN 178 IF @Operation ='LOP_MODIFY_ROW' 179 BEGIN 180 /* If it is @Operation Type is 'LOP_MODIFY_ROW' then it is very simple to recover the modified data. The old data is in [RowLog Contents 0] Field and modified data is in [RowLog Contents 1] Field. Simply replace it with the modified data and get the old data. 181 */ 182 INSERT INTO @ModifiedRawData ([PAGE ID],[Slot ID],[AllocUnitId],[RowLog Contents 0_var]) 183 SELECT TOP 1 @ConsolidatedPageID AS [PAGE ID],@Slotid AS [Slot ID],@AllocUnitID AS [AllocUnitId] 184 ,REPLACE (UPPER([RowLog Contents 0_var]),UPPER(CAST('' AS XML).value('xs:hexBinary(sql:variable("@RowLogContents1") )', 'varchar(max)')),UPPER(cast('' AS XML).value('xs:hexBinary(sql:variable("@RowLogContents0") )', 'varchar(max)'))) AS [RowLog Contents 0_var] 185 FROM @ModifiedRawData WHERE [PAGE ID]=@ConsolidatedPageID And [Slot ID]=@Slotid And [AllocUnitId]=@AllocUnitID ORDER BY [ID] DESC 186 187 --- Convert the old data which is in string format to hex format. 188 UPDATE @ModifiedRawData SET [RowLog Contents 0] = cast('' AS XML).value('xs:hexBinary(substring(sql:column("[RowLog Contents 0_var]"), 0) )', 'varbinary(max)') 189 FROM @ModifiedRawData Where [Slot ID]=@SlotID 190 191 END 192 IF @Operation ='LOP_MODIFY_COLUMNS' 193 BEGIN 194 195 /* If it is @Operation Type is 'LOP_MODIFY_ROW' then we need to follow a different procedure to recover modified 196 .Because this time the data is also in chunks but merge with the data log. 197 */ 198 --First, we need to get the [RowLog Contents 3] Because in [Log Record] field the modified data is available after the [RowLog Contents 3] data. 199 SET @RowLogContents3_Var=cast('' AS XML).value('xs:hexBinary(sql:variable("@RowLogContents3") )', 'varchar(max)') 200 SET @LogRecord_Var =cast('' AS XML).value('xs:hexBinary(sql:variable("@LogRecord"))', 'varchar(max)') 201 202 DECLARE @RowLogData_Var VARCHAR(Max) 203 DECLARE @RowLogData_Hex VARBINARY(Max) 204 ---First get the modifed data chunks in string format 205 SET @RowLogData_Var = SUBSTRING(@LogRecord_Var, CHARINDEX(@RowLogContents3_Var,@LogRecord_Var) +LEN(@RowLogContents3_Var) ,LEN(@LogRecord_Var)) 206 --Then convert it into the hex values. 207 SELECT @RowLogData_Hex=CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@RowLogData_Var"),0) )', 'varbinary(max)') 208 FROM (SELECT CASE SUBSTRING(@RowLogData_Var, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos) 209 DECLARE @TotalFixedLengthData INT 210 DECLARE @FixedLength_Offset INT 211 DECLARE @VariableLength_Offset INT 212 DECLARE @VariableLength_Offset_Start INT 213 DECLARE @VariableLengthIncrease INT 214 DECLARE @FixedLengthIncrease INT 215 DECLARE @OldFixedLengthStartPosition INT 216 DECLARE @FixedLength_Loc INT 217 DECLARE @VariableLength_Loc INT 218 DECLARE @FixedOldValues VARBINARY(MAX) 219 DECLARE @FixedNewValues VARBINARY(MAX) 220 DECLARE @VariableOldValues VARBINARY(MAX) 221 DECLARE @VariableNewValues VARBINARY(MAX) 222 223 -- Before recovering the modfied data we need to get the total fixed length data size and start position of the varaible data 224 225 SELECT TOP 1 @TotalFixedLengthData=CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0] , 2 + 1, 2)))) 226 ,@VariableLength_Offset_Start=CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0] , 2 + 1, 2))))+5+CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0] , CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0] , 2 + 1, 2)))) + 1, 2))))/8.0)) 227 FROM @ModifiedRawData 228 ORDER BY [ID] DESC 229 230 SET @FixedLength_Offset= CONVERT(BINARY(2),REVERSE(CONVERT(BINARY(4),(@RowLogContents0))))--) 231 SET @VariableLength_Offset=CONVERT(int,CONVERT(BINARY(2),REVERSE(@RowLogContents0))) 232 233 /* We already have modified data chunks in @RowLogData_Hex but this data is in merge format (modified plus actual data) 234 So , here we need [Row Log Contents 1] field , because in this field we have the data length both the modified and actual data 235 so this length will help us to break it into original and modified data chunks. 236 */ 237 SET @FixedLength_Loc= CONVERT(INT,SUBSTRING(@RowLogContents1,1,1)) 238 SET @VariableLength_Loc =CONVERT(INT,SUBSTRING(@RowLogContents1,3,1)) 239 240 /*First , we need to break Fix length data actual with the help of data length */ 241 SET @OldFixedLengthStartPosition= CHARINDEX(@RowLogContents4,@RowLogData_Hex) 242 SET @FixedOldValues = SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition,@FixedLength_Loc) 243 SET @FixedLengthIncrease = (CASE WHEN (Len(@FixedOldValues)%4)=0 THEN 1 ELSE (4-(LEN(@FixedOldValues)%4)) END) 244 /*After that , we need to break Fix length data modified data with the help of data length */ 245 SET @FixedNewValues =SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition+@FixedLength_Loc+@FixedLengthIncrease,@FixedLength_Loc) 246 247 /*Same we need to break the variable data with the help of data length*/ 248 SET @VariableOldValues =SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition+@FixedLength_Loc+@FixedLengthIncrease+@FixedLength_Loc+(@FixedLengthIncrease),@VariableLength_Loc) 249 SET @VariableLengthIncrease = (CASE WHEN (LEN(@VariableOldValues)%4)=0 THEN 1 ELSE (4-(Len(@VariableOldValues)%4))+1 END) 250 SET @VariableOldValues =(Case When @VariableLength_Loc =1 Then @VariableOldValues+0x00 else @VariableOldValues end) 251 252 SET @VariableNewValues =SUBSTRING(SUBSTRING(@RowLogData_Hex,@OldFixedLengthStartPosition+@FixedLength_Loc+@FixedLengthIncrease+@FixedLength_Loc+(@FixedLengthIncrease-1)+@VariableLength_Loc+@VariableLengthIncrease,Len(@RowLogData_Hex)+1),1,Len(@RowLogData_Hex)+1) --LEN(@VariableOldValues) 253 254 /*here we need to replace the fixed length & variable length actaul data with modifed data 255 */ 256 257 Select top 1 @VariableNewValues=Case 258 When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)+1),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)+1) 259 When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)) 260 When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)-1),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)-1)--3 --Substring(@VariableNewValues,0,Len(@VariableNewValues)-1) 261 When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)-2),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)-2) 262 When Charindex(Substring(@VariableNewValues,0,Len(@VariableNewValues)-3),[RowLog Contents 0])<>0 Then Substring(@VariableNewValues,0,Len(@VariableNewValues)-3) --5--Substring(@VariableNewValues,0,Len(@VariableNewValues)-3) 263 End 264 FROM @ModifiedRawData Where [Slot ID]=@SlotID ORDER BY [ID] DESC 265 266 INSERT INTO @ModifiedRawData ([PAGE ID],[Slot ID],[AllocUnitId],[RowLog Contents 0_var],[RowLog Contents 0]) 267 SELECT TOP 1 @ConsolidatedPageID AS [PAGE ID],@Slotid AS [Slot ID],@AllocUnitID AS [AllocUnitId],NULL 268 ,CAST(REPLACE(SUBSTRING([RowLog Contents 0],0,@TotalFixedLengthData+1),@FixedNewValues, @FixedOldValues) AS VARBINARY(max)) 269 + SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 1, 2) 270 + SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 3, CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 1, 2))))/8.0))) 271 + SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 3 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], @TotalFixedLengthData + 1, 2))))/8.0)), 2) 272 + Substring([RowLog Contents 0],@VariableLength_Offset_Start,(@VariableLength_Offset-(@VariableLength_Offset_Start-1))) 273 + CAST(REPLACE(SUBSTRING([RowLog Contents 0],@VariableLength_Offset+1,Len(@VariableNewValues)) 274 , @VariableNewValues 275 , @VariableOldValues) AS VARBINARY) 276 + Substring([RowLog Contents 0],@VariableLength_Offset+Len(@VariableNewValues)+1,LEN([RowLog Contents 0])) 277 FROM @ModifiedRawData Where [Slot ID]=@SlotID ORDER BY [ID] DESC 278 279 END 280 281 FETCH NEXT FROM Page_Data_Cursor INTO @ConsolidatedPageID, @Slotid,@AllocUnitID,@TransactionID,@RowLogContents0,@RowLogContents1,@RowLogContents3,@RowLogContents4,@LogRecord,@Operation 282 END 283 284 CLOSE Page_Data_Cursor 285 DEALLOCATE Page_Data_Cursor 286 287 DECLARE @RowLogContents VARBINARY(8000) 288 Declare @AllocUnitName NVARCHAR(Max) 289 Declare @SQL NVARCHAR(Max) 290 291 DECLARE @bitTable TABLE 292 ( 293 [ID] INT, 294 [Bitvalue] INT 295 ) 296 ----Create table to set the bit position of one byte. 297 298 INSERT INTO @bitTable 299 SELECT 0,2 UNION ALL 300 SELECT 1,2 UNION ALL 301 SELECT 2,4 UNION ALL 302 SELECT 3,8 UNION ALL 303 SELECT 4,16 UNION ALL 304 SELECT 5,32 UNION ALL 305 SELECT 6,64 UNION ALL 306 SELECT 7,128 307 308 --Create table to collect the row data. 309 DECLARE @DeletedRecords TABLE 310 ( 311 [ID] INT IDENTITY(1,1), 312 [RowLogContents] VARBINARY(8000), 313 [AllocUnitID] BIGINT, 314 [Transaction ID] NVARCHAR(Max), 315 [Slot ID] INT, 316 [FixedLengthData] SMALLINT, 317 [TotalNoOfCols] SMALLINT, 318 [NullBitMapLength] SMALLINT, 319 [NullBytes] VARBINARY(8000), 320 [TotalNoofVarCols] SMALLINT, 321 [ColumnOffsetArray] VARBINARY(8000), 322 [VarColumnStart] SMALLINT, 323 [NullBitMap] VARCHAR(MAX) 324 ) 325 --Create a common table expression to get all the row data plus how many bytes we have for each row. 326 ;WITH RowData AS ( 327 SELECT 328 329 [RowLog Contents 0] AS [RowLogContents] 330 331 ,@AllocUnitID AS [AllocUnitID] 332 333 ,[ID] AS [Transaction ID] 334 335 ,[Slot ID] as [Slot ID] 336 --[Fixed Length Data] = Substring (RowLog content 0, Status Bit A+ Status Bit B + 1,2 bytes) 337 ,CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) AS [FixedLengthData] --@FixedLengthData 338 339 --[TotalnoOfCols] = Substring (RowLog content 0, [Fixed Length Data] + 1,2 bytes) 340 ,CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 341 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2)))) as [TotalNoOfCols] 342 343 --[NullBitMapLength]=ceiling([Total No of Columns] /8.0) 344 ,CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 345 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)) as [NullBitMapLength] 346 347 --[Null Bytes] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [NullBitMapLength] ) 348 ,SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3, 349 CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 350 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0))) as [NullBytes] 351 352 --[TotalNoofVarCols] = Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 ) 353 ,(CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN 354 CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 355 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 356 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 357 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)), 2)))) ELSE null END) AS [TotalNoofVarCols] 358 359 --[ColumnOffsetArray]= Substring (RowLog content 0, Status Bit A+ Status Bit B + [Fixed Length Data] +1, [Null Bitmap length] + 2 , [TotalNoofVarCols]*2 ) 360 ,(CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN 361 SUBSTRING([RowLog Contents 0] 362 , CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 363 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 364 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)) + 2 365 , (CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN 366 CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 367 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 368 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 369 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)), 2)))) ELSE null END) 370 * 2) ELSE null END) AS [ColumnOffsetArray] 371 372 -- Variable column Start = Status Bit A+ Status Bit B + [Fixed Length Data] + [Null Bitmap length] + 2+([TotalNoofVarCols]*2) 373 ,CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1)In (0x30,0x70) 374 THEN ( 375 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 4 376 377 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 378 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)) 379 380 + ((CASE WHEN SUBSTRING([RowLog Contents 0], 1, 1) In (0x30,0x70) THEN 381 CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 382 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 3 383 + CONVERT(INT, ceiling(CONVERT(INT, CONVERT(BINARY(2), REVERSE(SUBSTRING([RowLog Contents 0], CONVERT(SMALLINT, CONVERT(BINARY(2) 384 ,REVERSE(SUBSTRING([RowLog Contents 0], 2 + 1, 2)))) + 1, 2))))/8.0)), 2)))) ELSE null END) * 2)) 385 386 ELSE null End AS [VarColumnStart] 387 From @ModifiedRawData 388 389 ), 390 391 ---Use this technique to repeate the row till the no of bytes of the row. 392 N1 (n) AS (SELECT 1 UNION ALL SELECT 1), 393 N2 (n) AS (SELECT 1 FROM N1 AS X, N1 AS Y), 394 N3 (n) AS (SELECT 1 FROM N2 AS X, N2 AS Y), 395 N4 (n) AS (SELECT ROW_NUMBER() OVER(ORDER BY X.n) 396 FROM N3 AS X, N3 AS Y) 397 398 insert into @DeletedRecords 399 Select RowLogContents 400 ,[AllocUnitID] 401 ,[Transaction ID] 402 ,[Slot ID] 403 ,[FixedLengthData] 404 ,[TotalNoOfCols] 405 ,[NullBitMapLength] 406 ,[NullBytes] 407 ,[TotalNoofVarCols] 408 ,[ColumnOffsetArray] 409 ,[VarColumnStart] 410 --Get the Null value against each column (1 means null zero means not null) 411 ,[NullBitMap]=(REPLACE(STUFF((SELECT ',' + 412 (CASE WHEN [ID]=0 THEN CONVERT(NVARCHAR(1),(SUBSTRING(NullBytes, n, 1) % 2)) ELSE CONVERT(NVARCHAR(1),((SUBSTRING(NullBytes, n, 1) / [Bitvalue]) % 2)) END) --as [nullBitMap] 413 FROM 414 N4 AS Nums 415 Join RowData AS C ON n<=NullBitMapLength 416 Cross Join @bitTable WHERE C.[RowLogContents]=D.[RowLogContents] ORDER BY [RowLogContents],n ASC FOR XML PATH('')),1,1,''),',','')) 417 FROM RowData D 418 419 CREATE TABLE [#temp_Data] 420 ( 421 422 [FieldName] VARCHAR(MAX) COLLATE database_default NOT NULL, 423 [FieldValue] VARCHAR(MAX) COLLATE database_default NULL, 424 [Rowlogcontents] VARBINARY(8000), 425 [Transaction ID] VARCHAR(MAX) COLLATE database_default NOT NULL, 426 [Slot ID] INT, 427 [NonID] INT, 428 --[System_type_id] int 429 430 ) 431 ---Create common table expression and join it with the rowdata table 432 --to get each column details 433 ;With CTE AS ( 434 /*This part is for variable data columns*/ 435 SELECT 436 A.[ID], 437 Rowlogcontents, 438 [Transaction ID], 439 [Slot ID], 440 NAME , 441 cols.leaf_null_bit AS nullbit, 442 leaf_offset, 443 ISNULL(syscolumns.length, cols.max_length) AS [length], 444 cols.system_type_id, 445 cols.leaf_bit_position AS bitpos, 446 ISNULL(syscolumns.xprec, cols.precision) AS xprec, 447 ISNULL(syscolumns.xscale, cols.scale) AS xscale, 448 SUBSTRING([nullBitMap], cols.leaf_null_bit, 1) AS is_null, 449 --Calculate the variable column size from the variable column offset array 450 (CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN 451 CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * leaf_offset*-1) - 1, 2)))) ELSE 0 END) AS [Column value Size], 452 453 ---Calculate the column length 454 (CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * (leaf_offset*-1)) - 1, 2)))) 455 - ISNULL(NULLIF(CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * ((leaf_offset*-1) - 1)) - 1, 2)))), 0), [varColumnStart]) 456 ELSE 0 END) AS [Column Length] 457 458 --Get the Hexa decimal value from the RowlogContent 459 --HexValue of the variable column=Substring([Column value Size] - [Column Length] + 1,[Column Length]) 460 --This is the data of your column but in the Hexvalue 461 ,CASE WHEN SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=1 THEN NULL ELSE 462 SUBSTRING(Rowlogcontents,((CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * leaf_offset*-1) - 1, 2)))) ELSE 0 END) 463 - ((CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * (leaf_offset*-1)) - 1, 2)))) 464 - ISNULL(NULLIF(CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * ((leaf_offset*-1) - 1)) - 1, 2)))), 0), [varColumnStart]) 465 ELSE 0 END))) + 1,((CASE WHEN leaf_offset<1 and SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=0 THEN CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * (leaf_offset*-1)) - 1, 2)))) 466 - ISNULL(NULLIF(CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (SUBSTRING ([ColumnOffsetArray], (2 * ((leaf_offset*-1) - 1)) - 1, 2)))), 0), [varColumnStart]) 467 ELSE 0 END))) END AS hex_Value 468 469 FROM @DeletedRecords A 470 Inner Join sys.allocation_units allocunits On A.[AllocUnitId]=allocunits.[Allocation_Unit_Id] 471 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3) 472 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 AND partitions.partition_id = allocunits.container_id) 473 INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id 474 LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id AND syscolumns.colid = cols.partition_column_id 475 WHERE leaf_offset<0 476 477 UNION 478 /*This part is for fixed data columns*/ 479 SELECT 480 A.[ID], 481 Rowlogcontents, 482 [Transaction ID], 483 [Slot ID], 484 NAME , 485 cols.leaf_null_bit AS nullbit, 486 leaf_offset, 487 ISNULL(syscolumns.length, cols.max_length) AS [length], 488 cols.system_type_id, 489 cols.leaf_bit_position AS bitpos, 490 ISNULL(syscolumns.xprec, cols.precision) AS xprec, 491 ISNULL(syscolumns.xscale, cols.scale) AS xscale, 492 SUBSTRING([nullBitMap], cols.leaf_null_bit, 1) AS is_null, 493 (SELECT TOP 1 ISNULL(SUM(CASE WHEN C.leaf_offset >1 THEN max_length ELSE 0 END),0) FROM 494 sys.system_internals_partition_columns C WHERE cols.partition_id =C.partition_id And C.leaf_null_bit<cols.leaf_null_bit)+5 AS [Column value Size], 495 syscolumns.length AS [Column Length] 496 497 ,CASE WHEN SUBSTRING([nullBitMap], cols.leaf_null_bit, 1)=1 THEN NULL ELSE 498 SUBSTRING 499 ( 500 Rowlogcontents,(SELECT TOP 1 ISNULL(SUM(CASE WHEN C.leaf_offset >1 THEN max_length ELSE 0 END),0) FROM 501 sys.system_internals_partition_columns C where cols.partition_id =C.partition_id And C.leaf_null_bit<cols.leaf_null_bit)+5 502 ,syscolumns.length) END AS hex_Value 503 FROM @DeletedRecords A 504 Inner Join sys.allocation_units allocunits ON A.[AllocUnitId]=allocunits.[Allocation_Unit_Id] 505 INNER JOIN sys.partitions partitions ON (allocunits.type IN (1, 3) 506 AND partitions.hobt_id = allocunits.container_id) OR (allocunits.type = 2 AND partitions.partition_id = allocunits.container_id) 507 INNER JOIN sys.system_internals_partition_columns cols ON cols.partition_id = partitions.partition_id 508 LEFT OUTER JOIN syscolumns ON syscolumns.id = partitions.object_id AND syscolumns.colid = cols.partition_column_id 509 WHERE leaf_offset>0 ) 510 511 --Converting data from Hexvalue to its orgional datatype. 512 --Implemented datatype conversion mechanism for each datatype 513 --Select * from sys.columns Where [object_id]=object_id('' + @SchemaName_n_TableName + '') 514 --Select * from CTE 515 516 INSERT INTO #temp_Data 517 SELECT 518 NAME, 519 CASE 520 WHEN system_type_id IN (231, 239) THEN LTRIM(RTRIM(CONVERT(NVARCHAR(max),hex_Value))) --NVARCHAR ,NCHAR 521 WHEN system_type_id IN (167,175) THEN LTRIM(RTRIM(CONVERT(VARCHAR(max),REPLACE(hex_Value, 0x00, 0x20)))) --VARCHAR,CHAR 522 WHEN system_type_id = 48 THEN CONVERT(VARCHAR(MAX), CONVERT(TINYINT, CONVERT(BINARY(1), REVERSE (hex_Value)))) --TINY INTEGER 523 WHEN system_type_id = 52 THEN CONVERT(VARCHAR(MAX), CONVERT(SMALLINT, CONVERT(BINARY(2), REVERSE (hex_Value)))) --SMALL INTEGER 524 WHEN system_type_id = 56 THEN CONVERT(VARCHAR(MAX), CONVERT(INT, CONVERT(BINARY(4), REVERSE(hex_Value)))) -- INTEGER 525 WHEN system_type_id = 127 THEN CONVERT(VARCHAR(MAX), CONVERT(BIGINT, CONVERT(BINARY(8), REVERSE(hex_Value))))-- BIG INTEGER 526 WHEN system_type_id = 61 Then CONVERT(VARCHAR(MAX),CONVERT(DATETIME,CONVERT(VARBINARY(8000),REVERSE (hex_Value))),100) --DATETIME 527 --WHEN system_type_id IN( 40) Then CONVERT(VARCHAR(MAX),CONVERT(DATE,CONVERT(VARBINARY(8000),(hex_Value))),100) --DATE This datatype only works for SQL Server 2008 528 WHEN system_type_id =58 Then CONVERT(VARCHAR(MAX),CONVERT(SMALLDATETIME,CONVERT(VARBINARY(8000),REVERSE(hex_Value))),100) --SMALL DATETIME 529 WHEN system_type_id = 108 THEN CONVERT(VARCHAR(MAX), CAST(CONVERT(NUMERIC(38,30), CONVERT(VARBINARY,CONVERT(VARBINARY,xprec)+CONVERT(VARBINARY,xscale))+CONVERT(VARBINARY(1),0) + hex_Value) as FLOAT)) --- NUMERIC 530 WHEN system_type_id In(60,122) THEN CONVERT(VARCHAR(MAX),Convert(MONEY,Convert(VARBINARY(8000),Reverse(hex_Value))),2) --MONEY,SMALLMONEY 531 WHEN system_type_id =106 THEN CONVERT(VARCHAR(MAX), CAST(CONVERT(Decimal(38,34), CONVERT(VARBINARY,Convert(VARBINARY,xprec)+CONVERT(VARBINARY,xscale))+CONVERT(VARBINARY(1),0) + hex_Value) as FLOAT)) --- DECIMAL 532 WHEN system_type_id = 104 THEN CONVERT(VARCHAR(MAX),CONVERT (BIT,CONVERT(BINARY(1), hex_Value)%2)) -- BIT 533 WHEN system_type_id =62 THEN RTRIM(LTRIM(STR(CONVERT(FLOAT,SIGN(CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT)) * (1.0 + (CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT) & 0x000FFFFFFFFFFFFF) * POWER(CAST(2 AS FLOAT), -52)) * POWER(CAST(2 AS FLOAT),((CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT) & 0x7ff0000000000000) / EXP(52 * LOG(2))-1023))),53,LEN(hex_Value)))) --- FLOAT 534 When system_type_id =59 THEN Left(LTRIM(STR(CAST(SIGN(CAST(Convert(VARBINARY(8000),REVERSE(hex_Value)) AS BIGINT))* (1.0 + (CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS BIGINT) & 0x007FFFFF) * POWER(CAST(2 AS Real), -23)) * POWER(CAST(2 AS Real),(((CAST(CONVERT(VARBINARY(8000),Reverse(hex_Value)) AS INT) )& 0x7f800000)/ EXP(23 * LOG(2))-127))AS REAL),23,23)),8) --Real 535 WHEN system_type_id In (165,173) THEN (CASE WHEN CHARINDEX(0x,cast('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))', 'VARBINARY(8000)')) = 0 THEN '0x' ELSE '' END) +cast('' AS XML).value('xs:hexBinary(sql:column("hex_Value"))', 'varchar(max)') -- BINARY,VARBINARY 536 WHEN system_type_id =36 THEN CONVERT(VARCHAR(MAX),CONVERT(UNIQUEIDENTIFIER,hex_Value)) --UNIQUEIDENTIFIER 537 END AS FieldValue 538 ,[Rowlogcontents] 539 ,[Transaction ID] 540 ,[Slot ID] 541 ,[ID] 542 FROM CTE ORDER BY nullbit 543 544 /*Create Update statement*/ 545 /*Now we have the modified and actual data as well*/ 546 /*We need to create the update statement in case of recovery*/ 547 548 ;With CTE AS (SELECT 549 (CASE 550 WHEN system_type_id In (167,175,189) THEN QUOTENAME([Name]) + '=' + ISNULL(+ '''' + [A].[FieldValue]+ '''','NULL')+ ' ,'+' ' 551 WHEN system_type_id In (231,239) THEN QUOTENAME([Name]) + '=' + ISNULL(+ 'N''' +[A].[FieldValue]+ '''','NULL')+ ' ,'+'' 552 WHEN system_type_id In (58,40,61,36) THEN QUOTENAME([Name]) + '=' + ISNULL(+ ''''+[A].[FieldValue]+ '''','NULL') + ' ,'+' ' 553 WHEN system_type_id In (48,52,56,59,60,62,104,106,108,122,127) THEN QUOTENAME([Name]) + '=' + ISNULL([A].[FieldValue],'NULL')+ ' ,'+' ' 554 END) as [Field] 555 ,A.[Slot ID] 556 ,A.[Transaction ID] as [Transaction ID] 557 ,'D' AS [Type] 558 ,[A].Rowlogcontents 559 ,[A].[NonID] 560 FROM #temp_Data AS [A] 561 INNER JOIN #temp_Data AS [B] ON [A].[FieldName]=[B].[FieldName] 562 AND [A].[Slot ID]=[B].[Slot ID] 563 --And [A].[Transaction ID]=[B].[Transaction ID]+1 564 AND [B].[Transaction ID]= (SELECT Min(Cast([Transaction ID] as int)) as [Transaction ID] FROM #temp_Data AS [C] 565 WHERE [A].[Slot ID]=[C].[Slot ID] 566 GROUP BY [Slot ID]) 567 INNER JOIN sys.columns [D] On [object_id]=object_id('' + @SchemaName_n_TableName + '') 568 AND A.[Fieldname] = D.[name] 569 WHERE ISNULL([A].[FieldValue],'')<>ISNULL([B].[FieldValue],'') 570 UNION ALL 571 572 SELECT(CASE 573 WHEN system_type_id In (167,175,189) THEN QUOTENAME([Name]) + '=' + ISNULL(+ '''' + [A].[FieldValue]+ '''','NULL')+ ' AND '+'' 574 WHEN system_type_id In (231,239) THEN QUOTENAME([Name]) + '='+ ISNULL(+ 'N''' +[A].[FieldValue]+ '''','NULL')+ ' AND '+'' 575 WHEN system_type_id In (58,40,61,36) THEN QUOTENAME([Name]) + '=' + ISNULL(+ ''''+[A].[FieldValue]+ '''','NULL') + ' AND '+'' 576 WHEN system_type_id In (48,52,56,59,60,62,104,106,108,122,127) THEN QUOTENAME([Name]) + '=' + ISNULL([A].[FieldValue],'NULL') + ' AND '+'' 577 END) AS [Field] 578 ,A.[Slot ID] 579 ,A.[Transaction ID] AS [Transaction ID] 580 ,'S' AS [Type] 581 ,[A].Rowlogcontents 582 ,[A].[NonID] 583 FROM #temp_Data AS [A] 584 INNER JOIN #temp_Data AS [B] ON [A].[FieldName]=[B].[FieldName] 585 AND [A].[Slot ID]=[B].[Slot ID] 586 --And [A].[Transaction ID]=[B].[Transaction ID]+1 587 AND [B].[Transaction ID]= (SELECT Min(Cast([Transaction ID] as int)) as [Transaction ID] FROM #temp_Data AS [C] 588 WHERE [A].[Slot ID]=[C].[Slot ID] 589 GROUP BY [Slot ID]) 590 INNER JOIN sys.columns [D] ON [object_id]=object_id('' + @SchemaName_n_TableName + '') 591 AND [A].[Fieldname]=D.[name] 592 WHERE ISNULL([A].[FieldValue],'')=ISNULL([B].[FieldValue],'') 593 AND A.[Transaction ID] NOT IN (SELECT Min(Cast([Transaction ID] as int)) as [Transaction ID] FROM #temp_Data AS [C] 594 WHERE [A].[Slot ID]=[C].[Slot ID] 595 GROUP BY [Slot ID]) 596 ) 597 598 ,CTEUpdateQuery AS (SELECT 'UPDATE ' + @SchemaName_n_TableName + ' SET ' + LEFT( 599 STUFF((SELECT ' ' + ISNULL([Field],'')+ ' ' FROM CTE B 600 WHERE A.[Slot ID]=B.[Slot ID] AND A.[Transaction ID]=B.[Transaction ID] And B.[Type]='D' FOR XML PATH('') ),1,1,''), 601 602 LEN(STUFF((SELECT ' ' +ISNULL([Field],'')+ ' ' FROM CTE B 603 WHERE A.[Slot ID]=B.[Slot ID] AND A.[Transaction ID]=B.[Transaction ID] And B.[Type]='D' FOR XML PATH('') ),1,1,'') )-2) 604 605 + ' WHERE ' + 606 607 LEFT(STUFF((SELECT ' ' +ISNULL([Field],'')+ ' ' FROM CTE C 608 WHERE A.[Slot ID]=C.[Slot ID] AND A.[Transaction ID]=C.[Transaction ID] And C.[Type]='S' FOR XML PATH('') ),1,1,'') , 609 610 LEN(STUFF((SELECT ' ' +ISNULL([Field],'')+ ' ' FROM CTE C 611 WHERE A.[Slot ID]=C.[Slot ID] AND A.[Transaction ID]=C.[Transaction ID] And C.[Type]='S' FOR XML PATH('') ),1,1,''))-4) 612 AS [Update Statement], 613 [Slot ID] 614 ,[Transaction ID] 615 ,Rowlogcontents 616 ,[A].[NonID] 617 FROM CTE A 618 GROUP BY [Slot ID] 619 ,[Transaction ID] 620 ,Rowlogcontents 621 ,[A].[NonID] ) 622 623 INSERT INTO #temp_Data 624 SELECT 'Update Statement',ISNULL([Update Statement],''),[Rowlogcontents],[Transaction ID],[Slot ID],[NonID] FROM CTEUpdateQuery 625 626 /**************************/ 627 --Create the column name in the same order to do pivot table. 628 DECLARE @FieldName VARCHAR(max) 629 SET @FieldName = STUFF( 630 ( 631 SELECT ',' + CAST(QUOTENAME([Name]) AS VARCHAR(MAX)) FROM syscolumns WHERE id=object_id('' + @SchemaName_n_TableName + '') 632 633 FOR XML PATH('') 634 ), 1, 1, '') 635 636 --Finally did pivot table and got the data back in the same format. 637 --The [Update Statement] column will give you the query that you can execute in case of recovery. 638 SET @sql = 'SELECT ' + @FieldName + ',[Update Statement] FROM #temp_Data 639 PIVOT (Min([FieldValue]) FOR FieldName IN (' + @FieldName + ',[Update Statement])) AS pvt 640 Where [Transaction ID] NOT In (Select Min(Cast([Transaction ID] as int)) as [Transaction ID] from #temp_Data 641 Group By [Slot ID]) ORDER BY Convert(int,[Slot ID]),Convert(int,[Transaction ID])' 642 Print @sql 643 EXEC sp_executesql @sql 644 645 GO 646 --Execute the procedure like 647 --Recover_Modified_Data_Proc 'Database name''Schema.table name','Date from' ,'Date to' 648 649 ----EXAMPLE #1 : FOR ALL MODIFIED RECORDS 650 EXEC Recover_Modified_Data_Proc 'test','dbo.Student' 651 GO 652 --EXAMPLE #2 : FOR ANY SPECIFIC DATE RANGE 653 EXEC Recover_Modified_Data_Proc 'test','dbo.Student','2000/01/01','9999/12/31' 654 --It will give you the result of all modified records.
View Code
转载于:https://www.cnblogs.com/xiaozizi/p/5953449.html
【sqlserver】在没有数据库备份的情况下,获得操作记录信息【code】相关推荐
- LANDESK操作系统部署时,网卡启动后,自动进入pe选择菜单,而不用按F8 进行选择启动菜单的情况下,如何操作?...
LANDESK操作系统部署时,网卡启动后,自动进入pe选择菜单,而不用按F8 进行选择启动菜单的情况下,如何操作? 设置方法: 使用PXE的控制队列 PXE的控制队列是另一个远程图像PXE的部署操作系 ...
- // D:\SaveLog\_SaveLog.dpr立即备份晓亮的电脑操作记录热键(快捷键) F11由于原来的 AutoIt 杀毒软件总是误报没办法只好麻烦一点用 Delphi XE4 做了...
// D:\SaveLog\_SaveLog.dpr立即备份晓亮的电脑操作记录 热键(快捷键) F11由于原来的 AutoIt 杀毒软件总是误报没办法只好麻烦一点用 Delphi 各种版本 做了 (我 ...
- _SaveLog.dpr立即备份晓亮的电脑操作记录热键(快捷键) F11由于原来的 AutoIt 杀毒软件总是误报...
{*******************************************************} { ...
- ;按F11立即备份晓亮的电脑操作记录并打开记录.AU3 (AutoIt)
2013-10-18 11:52:34 #region ;**** 参数创建于 ACNWrapper_GUI **** #PRE_UseUpx=n #PRE_Res_requestedExecuti ...
- 【黄啊码】MySQL入门—17、在没有备份的情况下,如何恢复数据库数据?
大家好!我是黄啊码,MySQL的入门篇已经讲到第16个课程了,今天我们继续讲讲大白篇系列--科技与狠活之恢复数据库 在没做数据库备份,没有开启使用 Binlog 的情况下,尽可能地找回数据. 今天的内 ...
- SQLServer中的数据库备份恢复并重命名
在做数据库备份时常常需要将A数据库备份,并恢复成一模一样的B数据库,但是在restore的时候SQL Server会自动式别.bak文件的数据文件和日志文件的逻辑名称(一般情况下是 A.mdf 和 A ...
- mysql设置停止二进制文件的操作_window下使用二进制文件对mysql数据库备份及恢复(再现用户操作)...
1,查看二进制日志文件是否启用 mysql>show variables like 'log_bin'; 如果未启用,则启用二进制文件(默认是关闭的 ) 2,在mysql的配置文件[mysqld ...
- MySQL将表中的yes改成no_mysql在不需要改程序的情况下通过操作数据库对单表数据量大的表进行分表...
1.为什么要分表? 数据库数据越来越大,随之而来的是单个表中数据太多.以至于查询速度变慢,而且由于表的锁机制导致应用操作也搜到严重影响,出现了数据库性能瓶颈. mysql中有一种机制是表锁定和行锁定, ...
- vertica 数据库 linux,CentOS 7下安装vertica记录
CentOS 7下安装vertica记录 1. 安装好centeros 并更新 Centeros安装就不说了,安装完之后联网环境下 yum update.更新下,使得那些包都是新的.(要想用中文 ...
最新文章
- 初探JavaScript魅力1
- centos6.4安装java,CentOS6.4下YUM安装MySQL和JDK和Tomcat
- Python计算机视觉:第六章 图像聚类
- 计算机技术会议排名,计算机学科会议排名
- 【存储过程】Merge Into语句实现Insert/Update在Oracle中的应用
- mysql表变量临时表_表变量和临时表详解
- Spring Data JPA 从入门到精通~javax.persistence概况介绍
- 实践2.2 内核模块编译
- php后缀名隐藏,php隐藏后缀名的方法是什么
- r语言结构方程模型可视化_R语言时间序列分析(二):ts对象及其可视化
- request.form 和 Request.QueryString 区别
- 廖雪峰python笔记
- axure原型图手机界面尺寸
- 视频去水印工具-视频去水印哪个软件好用免费
- A Survey of Zero-Shot Learning: Settings, Methods, and Applications [reading notes]
- 中国大学MOOC-陈越、何钦铭-数据结构-2020春期中考试
- 【Java】Map类
- Java中xml转义字符和gt,gte,lt,lte缩写
- vue animate bounceInRight 只执行一遍
- 惟伊·京汉方内部启动会圆满成功
热门文章
- EIGRP的successor与fessible successor
- Flex实现分页显示功能(mx:DataGrid)
- 工作中让你提建议怎么办?
- Querying an unbounded table ‘myhive.db1.datagen‘ in batch mode is not allowed
- CHUNGHOP k-6868万能空调遥控器的自己家用电器的代码+自己家里的宽带账号+机顶盒型号+桌子+椅子+垫子高度
- ubnutu18.10拔除硬盘后进行recovery mode
- ubuntu16.04下面xfce4没有声音
- SAFESEH 映像的模块不安全
- android word转html标签,如何将Word转换为网页html格式的方法(附代码清理方法)
- Machine Learning之Python篇(二)