检测SQL Server 2000表数据中的更改

时间:2012-05-03 09:38:13

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

我定期检查某个查询(顺便提一下包含多个表格),如果上次检查后的某些内容发生了变化(每天一次),则向用户添加信息性消息。

我尝试使其与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中的差异。

1 个答案:

答案 0 :(得分:1)

checksum_agg似乎只是为所有行添加binary_checksum的结果。虽然每行都有变化,但两个校验和的总和没有(即17 + 32 = 16 + 33)。这不是检查更新的常态,但我能提出的建议如下:

  1. 不是使用checksum_agg,而是将校验和连接成一个分隔的字符串,并按照SELECT binary_checksum(*) + ',' FROM MyTable FOR XML PATH('')的行比较字符串。要检查和存储的字符串要长得多,但误报率的可能性要小得多。
  2. 不使用内置校验和例程,而是使用HASHBYTES计算8000字节块中的MD5校验和,并将结果与​​xor一起计算。这将为您提供更具弹性的校验和,但仍然不是防弹的(即仍然可能得到错误的匹配,但非常不太可能)。我将粘贴下面写的HASHBYTES演示代码。
  3. 最后一个选项,也是绝对的最后手段,是以XML格式实际存储表格表,然后进行比较。这实际上是唯一可以绝对确定没有错误匹配的方法,但不具备可扩展性,并且涉及存储和比较大量数据。
  4. 每种方法,包括您开始使用的方法,都有利有弊,不同程度的数据大小和处理要求与准确性有关。根据所需的准确度级别,使用适当的选项。获得100%准确度的唯一方法是存储所有表数据。

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

    另一种方法是使用table_name(VARCHAR)和is_modified(BIT)字段创建一个已修改的表,其中包含您要跟踪的每个表的一行。使用insert,update和delete触发器,相对表的标志设置为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