0

如果自上次检查(每天一次)以来发生了变化,我会定期检查某个查询(顺便说一句,其中包括多个表),以便向用户添加信息性消息。

我试图让它与 一起工作checksum_agg(binary_checksum(*)),但它没有帮助,所以这个问题没有多大帮助,因为我有以下案例(过于简单化):

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 中的这些差异。

4

1 回答 1

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 字段,该字段使用 after insert 和 update 触发器设置为 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
于 2012-05-03T13:22:49.323 回答