如何修复可以恢复已删除数据的存储过程

How to fix a stored procedure that can recover deleted data

我最近偶然发现了一个 blog post,它谈到了一个名为 Recover_Deleted_Data_Proc.sql 的存储过程,它显然可以从 .log 文件中恢复已删除的数据。

太阳底下无新鲜事,we are going to use fn_dblog

重现步骤

我们首先要创建 table:

--Create Table
CREATE TABLE [Test_Table]
(
    [Col_image] image,
    [Col_text] text,
    [Col_uniqueidentifier] uniqueidentifier,
    [Col_tinyint] tinyint,
    [Col_smallint] smallint,
    [Col_int] int,
    [Col_smalldatetime] smalldatetime,
    [Col_real] real,
    [Col_money] money,
    [Col_datetime] datetime,
    [Col_float] float,
    [Col_Int_sql_variant] sql_variant,
    [Col_numeric_sql_variant] sql_variant,
    [Col_varchar_sql_variant] sql_variant,
    [Col_uniqueidentifier_sql_variant] sql_variant,
    [Col_Date_sql_variant] sql_variant,
    [Col_varbinary_sql_variant] sql_variant,
    [Col_ntext] ntext,
    [Col_bit] bit,
    [Col_decimal] decimal(18,4),
    [Col_numeric] numeric(18,4),
    [Col_smallmoney] smallmoney,
    [Col_bigint] bigint,
    [Col_varbinary] varbinary(Max),
    [Col_varchar] varchar(Max),
    [Col_binary] binary(8),
    [Col_char] char,
    [Col_timestamp] timestamp,
    [Col_nvarchar] nvarchar(Max),
    [Col_nchar] nchar,
    [Col_xml] xml,
    [Col_sysname] sysname
)

然后我们将数据插入其中:

--Insert data into it
INSERT INTO [Test_Table]
           ([Col_image]
           ,[Col_text]
           ,[Col_uniqueidentifier]
           ,[Col_tinyint]
           ,[Col_smallint]
           ,[Col_int]
           ,[Col_smalldatetime]
           ,[Col_real]
           ,[Col_money]
           ,[Col_datetime]
           ,[Col_float]
           ,[Col_Int_sql_variant]
           ,[Col_numeric_sql_variant]
           ,[Col_varchar_sql_variant]
           ,[Col_uniqueidentifier_sql_variant]
           ,[Col_Date_sql_variant]
           ,[Col_varbinary_sql_variant]
           ,[Col_ntext]
           ,[Col_bit]
           ,[Col_decimal]
           ,[Col_numeric]
           ,[Col_smallmoney]
           ,[Col_bigint]
           ,[Col_varbinary]
           ,[Col_varchar]
           ,[Col_binary]
           ,[Col_char]
           ,[Col_nvarchar]
           ,[Col_nchar]
           ,[Col_xml]
           ,[Col_sysname])
     VALUES
           (CONVERT(IMAGE,REPLICATE('A',4000))
           ,REPLICATE('B',8000)
           ,NEWID()
           ,10
           ,20
           ,3000
           ,GETDATE()
           ,4000
           ,5000
           ,getdate()+15
           ,66666.6666
           ,777777
           ,88888.8888
           ,REPLICATE('C',8000)
           ,newid()
           ,getdate()+30
           ,CONVERT(VARBINARY(8000),REPLICATE('D',8000))
           ,REPLICATE('E',4000)
           ,1
           ,99999.9999
           ,10101.1111
           ,1100
           ,123456
           ,CONVERT(VARBINARY(MAX),REPLICATE('F',8000))
           ,REPLICATE('G',8000)
           ,0x4646464
           ,'H'
           ,REPLICATE('I',4000)
           ,'J'
           ,CONVERT(XML,REPLICATE('K',4000))
           ,REPLICATE('L',100)
           )
 
GO

我们现在要验证数据是否存在:

--Verify the data
SELECT * FROM Test_Table

此时我们需要创建存储过程。我无法将它粘贴在这里,因为它太长了,但你可以从同一个 blog post 下载它,其中有一个 link 到一个 Box 文件 .

如果查询给您带来这样的麻烦:

Msg 50000, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 22 [Batch Start Line 700]   The compatibility level should be equal to or greater SQL SERVER 2005 (90)

Msg 50000, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 22 [Batch Start Line 705]   The compatibility level should be equal to or greater SQL SERVER 2005 (90)

是因为你要注释掉第701行到第708行。

太棒了,现在让我们从 table:

中删除数据
--Delete the data
DELETE FROM Test_Table

并确认数据已删除:

--Verify the data
SELECT * FROM Test_Table

这是最后一步:我们需要尝试使用新安装的存储过程恢复数据。

作者指示我们使用这两个命令之一(不要忘记将 'test' 更改为您的数据库名称):

--Recover the deleted data without date range
EXEC Recover_Deleted_Data_Proc 'test', 'dbo.Test_Table'

--Recover the deleted data it with date range
EXEC Recover_Deleted_Data_Proc 'test', 'dbo.Test_Table', '2012-06-01', '2012-06-30'

但问题是两者都returns这个错误:

(8 rows affected)

(2 rows affected)

(64 rows affected)

(2 rows affected)

(1 row affected)

(1 row affected)

(1 row affected)

(1 row affected)

(1 row affected)

(1 row affected)

Msg 245, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 485 [Batch Start Line 112]   
Conversion failed when converting the varchar value '0x41-->01 ; 0001' to data type int.

如果我右键单击存储过程并单击“修改”,我在 Line 485 处没有看到任何特别可疑的内容。

知道为什么这个存储过程不起作用吗?

提到的转换是什么?

该代码已有 10 年历史,编写时假设 [PAGE ID] 只能表示为一对整数,例如0001:00000138 - 但是,如您所知,有时表达方式不同,例如 0x41-->01 ; 0001:00000138.

您可以通过在光标内添加以下内容来解决该问题:

IF @ConsolidatedPageID LIKE '0x%-->%;%' 
BEGIN
  SET @ConsolidatedPageID = LTRIM(SUBSTRING(@ConsolidatedPageID, 
       CHARINDEX(';', @ConsolidatedPageID) + 1, 8000));
END

但是你的下一个问题是,当你从盒子文件中保存程序时,它可能 '†' 变成了一些古怪的 ? 字符。当我修复它时(当然使用 N'†',因为 Unicode 字符应该总是 N),我仍然收到这些错误消息:

Msg 537, Level 16, State 3, Procedure Recover_Deleted_Data_Proc, Line 525
Invalid length parameter passed to the LEFT or SUBSTRING function.
Msg 9420, Level 16, State 1, Procedure Recover_Deleted_Data_Proc, Line 651
XML parsing: line 1, character 2, illegal xml character

尝试对这道意大利面进行逆向工程 15 分钟后,我放弃了。如果您需要恢复已删除的数据,请恢复备份。如果您没有备份,那么,这就是我们进行备份的原因。人们试图创建脆弱的脚本来补偿不进行备份正是日志恢复供应商收取高额费用的原因。


顺便说一句,兼容性级别的错误消息是转移注意力的信息,完全误导了当前编写的逻辑,并且与问题完全无关。但它可以解决,如果,就在这之前:

IF ISNULL(@Compatibility_Level,0)<=80
BEGIN
  RAISERROR('The compatibility level should ... blah blah',16,1)
  RETURN
END

你添加这个:

IF DB_ID(@Database_Name) IS NULL
BEGIN
  RAISERROR(N'Database %s does not exist.',11,1,@Database_name);
  RETURN;
END

或者干脆 不在脚本末尾调用这两个示例调用,因为它们依赖于您有一个名为 test 的数据库,显然您没有.