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