【问题标题】:Detecting changes in SQL Server 2000 table data检测 SQL Server 2000 表数据中的更改
【发布时间】:2012-05-12 19:32:45
【问题描述】:

我定期检查某个查询(顺便说一下,其中包括多个表),以便在自上次检查(每天一次)后发生更改时向用户添加信息性消息。

我试图使它与checksum_agg(binary_checksum(*)) 一起工作,但它没有帮助,所以this question 没有多大帮助,因为我有以下情况(过于简单):

select  checksum_agg(binary_checksum(*))
from    
(
    select  1 as id,
            1 as status

    union all

    select  2 as id,
            0 as status
) data

select  checksum_agg(binary_checksum(*))
from    
(
    select  1 as id,
            0 as status

    union all

    select  2 as id,
            1 as status
) data

以上两种情况的校验和都是一样的,都是49,很明显数据已经改变了。

这不一定是一个简单的功能或简单的解决方案,但我需要一些方法来唯一地识别 SQL Server 2000 中的这些差异。

【问题讨论】:

标签: sql-server hash sql-server-2000 checksum


【解决方案1】:

checksum_agg 似乎只是将所有行的 binary_checksum 的结果相加。尽管每一行都发生了变化,但两个校验和的总和没有变化(即 17+32 = 16+33)。这并不是检查更新的真正标准,但我能提出的建议如下:

  1. 不使用checksum_agg,而是将校验和连接成一个分隔字符串,并按照SELECT binary_checksum(*) + ',' FROM MyTable FOR XML PATH('') 的行比较字符串。要检查和存储的字符串要长得多,但误报比较的可能性要小得多。
  2. 不使用内置校验和例程,而是使用 HASHBYTES 计算 8000 字节块中的 MD5 校验和,并将结果相异或。这将为您提供更具弹性的校验和,尽管仍然不是防弹的(即仍然有可能得到错误的匹配,但可能性很小)。我将粘贴我在下面编写的 HASHBYTES 演示代码。
  3. 最后的选择,也是绝对的最后手段,是以 XML 格式实际存储表表,然后进行比较。这确实是您可以绝对确定没有错误匹配的唯一方法,但不可扩展并且涉及存储和比较大量数据。

每种方法(包括您开始使用的方法)都有利有弊,数据大小和处理要求对准确性的要求各不相同。根据您需要的准确度级别,使用适当的选项。获得 100% 准确率的唯一方法是存储所有表数据。

或者,您可以为每个表添加一个 date_modified 字段,该字段设置为 GetDate() 使用后插入和更新触发器。你可以做SELECT COUNT(*) FROM #test WHERE date_modified > @date_last_checked。这是一种更常见的检查更新的方法。这个的缺点是无法跟踪删除。

另一种方法是创建一个修改后的表,其中包含 table_name (VARCHAR) 和 is_modified (BIT) 字段,其中包含您希望跟踪的每个表的一行。使用插入、更新和删除触发器,针对相关表的标志设置为 True。当你运行你的计划时,你检查并重置 is_modified 标志(在同一个事务中) - 沿着SELECT @is_modified = is_modified, is_modified = 0 FROM tblModified

以下脚本生成三个结果集,每个结果集都与此响应中前面的编号列表相对应。我已经在 SELECT 语句之前评论了哪个输出对应于哪个选项。要查看输出是如何得出的,您可以通过代码向后工作。

-- Create the test table and populate it
CREATE TABLE #Test (
    f1 INT,
    f2 INT
)
INSERT INTO #Test VALUES(1, 1)
INSERT INTO #Test VALUES(2, 0)
INSERT INTO #Test VALUES(2, 1)

/*******************
OPTION 1
*******************/
SELECT CAST(binary_checksum(*) AS VARCHAR) + ',' FROM #test FOR XML PATH('')

-- Declaration: Input and output MD5 checksums (@in and @out), input string (@input), and counter (@i)
DECLARE @in VARBINARY(16), @out VARBINARY(16), @input VARCHAR(MAX), @i INT

-- Initialize @input string as the XML dump of the table
-- Use this as your comparison string if you choose to not use the MD5 checksum
SET @input = (SELECT * FROM #Test FOR XML RAW)

/*******************
OPTION 3
*******************/
SELECT @input

-- Initialise counter and output MD5.
SET @i = 1
SET @out = 0x00000000000000000000000000000000
WHILE @i <= LEN(@input)
BEGIN
    -- calculate MD5 for this batch
    SET @in = HASHBYTES('MD5', SUBSTRING(@input, @i, CASE WHEN LEN(@input) - @i > 8000 THEN 8000 ELSE LEN(@input) - @i END))
    -- xor the results with the output
    SET @out = CAST(CAST(SUBSTRING(@in, 1, 4) AS INT) ^ CAST(SUBSTRING(@out, 1, 4) AS INT) AS VARBINARY(4)) +
        CAST(CAST(SUBSTRING(@in, 5, 4) AS INT) ^ CAST(SUBSTRING(@out, 5, 4) AS INT) AS VARBINARY(4)) +
        CAST(CAST(SUBSTRING(@in, 9, 4) AS INT) ^ CAST(SUBSTRING(@out, 9, 4) AS INT) AS VARBINARY(4)) +
        CAST(CAST(SUBSTRING(@in, 13, 4) AS INT) ^ CAST(SUBSTRING(@out, 13, 4) AS INT) AS VARBINARY(4))
    SET @i = @i + 8000
END

/*******************
OPTION 2
*******************/
SELECT @out

【讨论】:

  • 很遗憾,'hashbytes' 在 SQL Server 2000 中不是可识别的函数名称。
  • 啊啊 SQL2000。我错过了那个!我在这里找到了 T-SQL 的 MD 实现:hackchina.com/en/r/184114/MD5.sql__html。为疏忽道歉。
  • 我不相信有人真的写了这个,感谢脚本和解决方案。
  • 只是给所有阅读此问题的人的注意事项:我将问题中的查询中的checksum_agg(binary_checksum(*)) 替换为checksum_agg(binary_checksum(dbo.MD5(cast(checksum(*) as nvarchar(4000))))),这实际上可以解决我的问题...
猜你喜欢
  • 1970-01-01
  • 2015-06-24
  • 1970-01-01
  • 1970-01-01
  • 2014-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-18
相关资源
最近更新 更多