2

我试图快速确定两组时间表是否相同,并生成一个可以引用这些独特时间表的密钥。我最初尝试使用 HASHBYTES,但很快发现您只能散列 8000 个字符,而且我有大量的日期时间,当它们连接时超过 8000。

因此,我尝试使用 Checksum 和 Checksum_Agg,因为它们似乎是为这类事情设计的。我知道校验和更有可能生成非唯一值。但是我需要将它们相互比较的范围/上下文太窄了,我想我可以侥幸逃脱。

不幸的是,经过一点点测试后,我了解到我可以在 4 行日期时间数据中找到校验和“冲突”!我觉得这有点奇怪,并发现了碰撞的模式。

下面是一个演示该问题的示例脚本:

声明@Rows 表
(
    [GroupId] 整数,
    [开始日期] 日期时间,
    [结束日期] DATETIME
)

--Group1
插入@Rows 值(1、'2013-01-20 01:00:00.000'、'2013-01-20 01:20:00.000')
插入@Rows 值(1,'2013-01-20 01:20:00.000','2013-01-20 01:40:00.000')
--INSERT INTO @Rows 值 (1, '2013-01-20 01:40:00.000', '2013-01-20 02:00:00.000')
--INSERT INTO @Rows 值 (1, '2013-01-20 02:00:00.000', '2013-01-20 02:20:00.000')
--INSERT INTO @Rows 值 (1, '2013-01-20 02:20:00.000', '2013-01-20 02:40:00.000')
--INSERT INTO @Rows 值 (1, '2013-01-20 02:40:00.000', '2013-01-20 03:00:00.000')

--Group2
插入@Rows 值(2、'2013-01-21 01:00:00.000'、'2013-01-21 01:20:00.000')
插入@Rows 值(2、'2013-01-21 01:20:00.000'、'2013-01-21 01:40:00.000')
--INSERT INTO @Rows 值 (2, '2013-01-21 01:40:00.000', '2013-01-21 02:00:00.000')
--INSERT INTO @Rows 值 (2, '2013-01-21 02:00:00.000', '2013-01-21 02:20:00.000')
--INSERT INTO @Rows 值 (2, '2013-01-21 02:20:00.000', '2013-01-21 02:40:00.000')
--INSERT INTO @Rows 值 (2, '2013-01-21 02:40:00.000', '2013-01-21 03:00:00.000')

选择 [ChecksumAgg1] = CHECKSUM_AGG([CheckSum])
从
(
    选择 [校验和] = 校验和([开始日期],[结束日期])
    来自@Rows
    其中 GroupId = 1
) G1

选择 [ChecksumAgg2] = CHECKSUM_AGG([CheckSum])
从
(
    选择 [校验和] = 校验和([开始日期],[结束日期])
    来自@Rows
    其中 GroupId = 2
) G2

结果是:

校验和Agg1:5681728

校验和Agg2:5681728

这两个日期系列之间的唯一区别是它们相隔 1 天。但是它们会生成相同的校验和!但仅当行数为偶数时。如果您取消注释 Group 1 中的 INSERT 和 Group 2 中的一个 INSERT,您将获得两个不同的校验和。但是然后取消评论另一对,您将获得另一场比赛!


最后我有两个问题。我很想更多地了解它是如何工作的,以及为什么这种模式似乎会影响一个相当可预测的校验和值。更重要的是,我想知道是否有更好的方法来创建大量数据的“指纹”。我知道我不能保证这个哈希是全球唯一的,但我显然需要比校验和更好的东西。

我能够欺骗校验和计算的一种方法是在 Datetime 上执行 HASHBYTES,然后将其提供给 Checksum 函数。通过这种方式,校验和被提供的值比一组具有相似外观差异的日期看起来更随机。但这足够了吗?


编辑-这里只是更多的上下文。

基本上我有一个系统,它有大量的时间表数据和一个单独的系统,它在特定时间对这些时间表感兴趣。例如,多个用户可能会看到这个复杂时间表的某个部分的特定版本,并希望添加一些元数据(可能是他们的批准状态、注释或其他内容)。如果某些外部来源对任何单个日期时间进行了更改,则需要断开此链接,因为它不再是相同的时间表!

有许多不同的系统可以对核心调度数据进行更改,这就是为什么我很难将这种担忧提升到代码级别,以便以某种方式管理并将其“规范化”为以某种方式表示每个快照的实体。我必须在一百万个地方安装挂钩来监听变化,然后清理任何指向时间表的东西。

4

3 回答 3

2

你认为所有这些校验和的东西——考虑到你还必须做些什么来确保唯一性——值得麻烦吗?就我个人而言,我认为只需直接比较列而不是尝试减少工作并仅比较一个值,您将获得更好的性能(并且复杂性更低)。

另请记住,当您深入了解日期时间值时,它只是成对的整数,因此将校验和应用于两个值的组合可能会导致相同的值,这并不奇怪。例如:

SELECT CHECKSUM_AGG(x)
FROM
(
  SELECT CHECKSUM(1,2)
  UNION ALL 
  SELECT CHECKSUM(2,3)
) AS y(x);

SELECT CHECKSUM_AGG(x)
FROM
(
  SELECT CHECKSUM(2,2)
  UNION ALL 
  SELECT CHECKSUM(1,3)
) AS y(x);

结果:

----
49

----
49

所以我建议只是放一个索引StartDate, EndDate并完成它。你正试图让已经非常高效的东西变得更有效率,我认为你正在完成相反的事情。

至于键,只需使用一IDENTITY列或其他一些代理。我认为嵌套CHECKSUM_AGG(CHECKSUM(HASHBYTES(col1),HASHBYTES(col2)))来模拟唯一性没有任何优势......

编辑

或者考虑到新的要求,ROWVERSION如果您想确保数据与上次读取时的数据相同,只需使用一列。我看不出跟踪数百万校验和结果与跟踪 rowversion 或其他计算值有何不同。当已经有内置的东西可以做你想做的事情时,你的工作太辛苦了......

于 2013-01-22T02:17:07.123 回答
1

从此页面上的评论:

http://msdn.microsoft.com/en-us/library/ms188920.aspx

看来 Checksum_Agg 是使用 XOR 构建的。关于 XOR 的问题是,它们往往很容易通过包含两次相同的数字来逆转。这就解释了为什么你只在它均匀时才注意到它。

只要您知道 XOR 问题并以混合所有位的方式预先打乱您提供给它的内容,您就应该没问题。

于 2013-01-22T02:08:36.250 回答
0

我也面临这个问题。当您在列中的所有值都相同时,就会出现这种情况。在计算总和时,它可能不占用此列。

于 2014-08-27T08:39:00.000 回答