sql-server - 为什么这些日期时间值返回相同的 Checksum 和 Checksum_Agg?怎样才能让它更加独特呢?

标签 sql-server hash checksum

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

因此,我尝试使用 Checksum 和 Checksum_Agg 因为它们似乎是为此类事情而设计的。我知道 Checksum 有更高的机会生成非唯一值。但我需要将这些内容进行相互比较的范围/背景非常狭窄,我认为我可以摆脱它。

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

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

DECLARE @Rows TABLE
(
    [GroupId] INT,
    [StartDate] DATETIME,
    [EndDate] DATETIME
)

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

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

SELECT [ChecksumAgg1] = CHECKSUM_AGG([CheckSum])
FROM
(
    SELECT [CheckSum] = CHECKSUM([StartDate], [EndDate])
    FROM @Rows
    WHERE GroupId = 1
) G1

SELECT [ChecksumAgg2] = CHECKSUM_AGG([CheckSum])
FROM
(
    SELECT [CheckSum] = CHECKSUM([StartDate], [EndDate])
    FROM @Rows
    WHERE GroupId = 2
) G2

结果是:

ChecksumAgg1:5681728

ChecksumAgg2:5681728

这两个日期系列之间的唯一区别是它们相隔 1 天。但它们生成相同的校验和!但仅当有偶数行时。如果您取消注释来自第 1 组的 INSERT 和来自第 2 组的 INSERT,您将得到两个不同的校验和。但是,如果取消注释另一对,您将获得另一场比赛!


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

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


编辑 - 这里有更多背景信息。

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

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

最佳答案

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

另请记住,当您仔细考虑时,日期时间值只是整数对,因此将校验和应用于两个值的组合可能会产生相同的值,这并不奇怪。例如:

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 上放置一个索引并完成它。您试图使已经相当高效的事情变得更加高效,但我认为您正在实现相反的目标。

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

编辑

或者考虑到新的要求,只需使用 ROWVERSION如果您想确保数据与上次读取的数据相同,请查看列。我不明白跟踪数百万个校验和结果与跟踪行版本或其他计算值有什么不同。当已经有内置的东西可以完成您想要做的事情时,您的工作就太辛苦了...

关于sql-server - 为什么这些日期时间值返回相同的 Checksum 和 Checksum_Agg?怎样才能让它更加独特呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14450415/

相关文章:

sql-server - 不允许在另一个计算列定义中使用计算列

c++ - 哈希函数无法正常工作

c++ - 如何生成 24 个字符的 UUID?

sql-server - CHECKSUM 和 CHECKSUM_AGG : What's the algorithm?

dart - 为什么我的 asm 校验和宏的 Dart 实现不起作用?

sql - 如何在 SQL Server 中比较两列是否相等?

sql - 如何在SQL Server中将自定义字符串转换为日期

sql - 在一个元素中返回 SQL Server 中的逗号分隔值

c++ - 目录校验和

arrays - perl 组使用 fetchrow_hashref 生成散列