Luk*_*zda 10 sql t-sql sql-server
我想知道为什么BINARY_CHECKSUM
函数返回相同的不同结果:
SELECT *, BINARY_CHECKSUM(a,b) AS bc
FROM (VALUES(1, NULL, 100),
(2, NULL, NULL),
(3, 1, 2)) s(id,a,b);
SELECT *, BINARY_CHECKSUM(a,b) AS bc
FROM (VALUES(1, NULL, 100),
(2, NULL, NULL)) s(id,a,b);
Run Code Online (Sandbox Code Playgroud)
输出继电器:
+-----+----+------+-------------+
| id | a | b | bc |
+-----+----+------+-------------+
| 1 | | 100 | -109 |
| 2 | | | -2147483640 |
| 3 | 1 | 2 | 18 |
+-----+----+------+-------------+
-- -109 vs 100
+-----+----+------+------------+
| id | a | b | bc |
+-----+----+------+------------+
| 1 | | 100 | 100 |
| 2 | | | 2147483647 |
+-----+----+------+------------+
Run Code Online (Sandbox Code Playgroud)
对于第二个样本,我得到了我期望的结果:
SELECT *, BINARY_CHECKSUM(a,b) AS bc
FROM (VALUES(1, 1, 100),
(2, 3, 4),
(3,1,1)) s(id,a,b);
SELECT *, BINARY_CHECKSUM(a,b) AS bc
FROM (VALUES(1, 1, 100),
(2, 3, 4)) s(id,a,b);
Run Code Online (Sandbox Code Playgroud)
前两行的Ouptut:
+-----+----+------+-----+
| id | a | b | bc |
+-----+----+------+-----+
| 1 | 1 | 100 | 116 |
| 2 | 3 | 4 | 52 |
+-----+----+------+-----+
Run Code Online (Sandbox Code Playgroud)
当我想比较两个表/查询时,它会产生奇怪的后果:
WITH t AS (
SELECT 1 AS id, NULL AS a, 100 b
UNION ALL SELECT 2, NULL, NULL
UNION ALL SELECT 3, 1, 2 -- comment this out
), s AS (
SELECT 1 AS id ,100 AS a, NULL as b
UNION ALL SELECT 2, NULL, NULL
UNION ALL SELECT 3, 2, 1 -- comment this out
)
SELECT t.*,s.*
,BINARY_CHECKSUM(t.a, t.b) AS bc_t, BINARY_CHECKSUM(s.a, s.b) AS bc_s
FROM t
JOIN s
ON s.id = t.id
WHERE BINARY_CHECKSUM(t.a, t.b) = BINARY_CHECKSUM(s.a, s.b);
Run Code Online (Sandbox Code Playgroud)
对于3行,我得到单个结果:
+-----+----+----+-----+----+----+--------------+-------------+
| id | a | b | id | a | b | bc_t | bc_s |
+-----+----+----+-----+----+----+--------------+-------------+
| 2 | | | 2 | | | -2147483640 | -2147483640 |
+-----+----+----+-----+----+----+--------------+-------------+
Run Code Online (Sandbox Code Playgroud)
但对于2行我也得到id = 1:
+-----+----+------+-----+------+----+-------------+------------+
| id | a | b | id | a | b | bc_t | bc_s |
+-----+----+------+-----+------+----+-------------+------------+
| 1 | | 100 | 1 | 100 | | 100 | 100 |
| 2 | | | 2 | | | 2147483647 | 2147483647 |
+-----+----+------+-----+------+----+-------------+------------+
Run Code Online (Sandbox Code Playgroud)
备注:
我不是在寻找替代品(HASH_BYTES/MD5/CHECKSUM)
我知道BINARY_CHECKSUM
可能导致冲突(两个不同的调用产生相同的输出)这里的情况有点不同
对于此定义,我们说指定类型的空值比较为相等的值.如果表达式列表中的至少一个值发生更改,则表达式校验和也会更改.但是,这不能保证.因此,为了检测值是否已更改,我们建议仅在应用程序可以容忍偶尔错过更改时才使用BINARY_CHECKSUM.
对我来说很奇怪,哈希函数为相同的输入参数返回不同的结果.这种行为是设计还是某种故障?
编辑:
正如@scsimon 指出它适用于物化表但不适用于cte. db <>小提琴实际表格
cte的元数据:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set('
SELECT *
FROM (VALUES(1, NULL, 100),
(2, NULL, NULL),
(3, 1, 2)) s(id,a,b)', NULL,0);
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set('
SELECT *
FROM (VALUES(1, NULL, 100),
(2, NULL, NULL)) s(id,a,b)', NULL,0)
-- working workaround
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set('
SELECT *
FROM (VALUES(1, cast(NULL as int), 100),
(2, NULL, NULL)) s(id,a,b)', NULL,0)
Run Code Online (Sandbox Code Playgroud)
对于所有情况,所有列都是INT
明确的,CAST
它的行为应该如此.
这与行数无关。这是因为 2 行版本的其中一列中的值始终为NULL
。的默认类型NULL
是int
,数字常量(此长度)的默认类型是int
,因此它们应该是可比较的。但从values()
派生表来看,它们(显然)不是完全相同的类型。
特别是,NULL
派生表中仅包含 typeless 的列是不可比较的,因此它被排除在二进制校验和计算之外。这不会发生在真实的表中,因为所有列都有类型。
答案的其余部分说明了正在发生的事情。
该代码的行为与类型转换的预期一致:
SELECT *, BINARY_CHECKSUM(a, b) AS bc
FROM (VALUES(1, cast(NULL as int), 100),
(2, NULL, NULL)
) s(id,a,b);
Run Code Online (Sandbox Code Playgroud)
这是一个 db<>fiddle。
实际上使用值创建表表明仅包含NULL
值的列与包含显式数字的列具有完全相同的类型。这表明原始代码应该可以工作。但显式强制转换也可以解决这个问题。很奇怪。
这真的非常非常奇怪。考虑以下:
select v.*, checksum(a, b), checksum(c,b)
FROM (VALUES(1, NULL, 100, NULL),
(2, 1, 2, 1.0)
) v(id, a, b, c);
Run Code Online (Sandbox Code Playgroud)
“d”类型的更改会影响binary_checksum()
第二行,但不会影响第一行。
这是我的结论。当列中的所有值都是二进制时,则binary_checksum()
意识到这一点并且该列属于“不可比较数据类型”类别。然后校验和基于剩余的列。
您可以通过运行时查看错误来验证这一点:
select v.*, binary_checksum(a)
FROM (VALUES(1, NULL, 100, NULL),
(2, NULL, 2, 1.0)
) v( id,a, b, c);
Run Code Online (Sandbox Code Playgroud)
它抱怨说:
参数数据类型 NULL 对于校验和函数的参数 1 无效。
具有讽刺意味的是,如果将结果保存到表中并使用binary_checksum()
. 问题似乎是与values()
和 数据类型的某些交互 - 但在表中并不是很明显information_schema.columns
。
令人高兴的消息是,代码应该适用于表,即使它不适用于values()
生成的派生表 - 正如此SQL Fiddle 所示。
我还了解到,填充 s 的列NULL
实际上是无类型的。int
a 中数据类型的分配select into
似乎是在定义表时发生的。“无类型”类型被转换为int
.