背景:我试图在创建虚拟数据时获得一些随机的“十六进制”值,并提出了这种结构:
SELECT TOP 100 result = (CASE ABS(Binary_Checksum(NewID())) % 16 WHEN -1 THEN 'hello' WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' WHEN 3 THEN '3' WHEN 4 THEN '4' WHEN 5 THEN '5' WHEN 6 THEN '6' WHEN 7 THEN '7' WHEN 8 THEN '8' WHEN 9 THEN '9' WHEN 10 THEN 'a' WHEN 11 THEN 'b' WHEN 12 THEN 'c' WHEN 13 THEN 'd' WHEN 14 THEN 'e' WHEN 15 THEN 'f' ELSE 'huh' END) FROM sys.objects
在我的sql Server 2008 R2实例上运行时,我得到了很多’呵呵’的记录:
result ------ huh 3 huh huh 6 8 6
我真的不明白为什么.
我期望发生的是:
>对于每条记录,NewID()都会出现一个新的随机值
> Binary_Checksum()根据所述值计算int
> ABS()使价值为正
>%16如果除以16则返回该正值的余数,然后该值将是0到15之间的值
> CASE构造将值转换为相关字符
>由于0到15之间的每个值都有WHEN,因此永远不需要ELSE
或者至少,这就是我认为应该发生的事情……但显然在路上出了问题……
当用两步法(通过临时表)做同样的事情时,哼哼……
SELECT TOP 100 x = ABS(Binary_Checksum(NewID())) % 16,result = 'hello' INTO #test FROM sys.objects UPDATE #test SET result = (CASE x WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' WHEN 3 THEN '3' WHEN 4 THEN '4' WHEN 5 THEN '5' WHEN 6 THEN '6' WHEN 7 THEN '7' WHEN 8 THEN '8' WHEN 9 THEN '9' WHEN 10 THEN 'a' WHEN 11 THEN 'b' WHEN 12 THEN 'c' WHEN 13 THEN 'd' WHEN 14 THEN 'e' WHEN 15 THEN 'f' ELSE 'huh' END) SELECT * FROM #test
有谁理解这个?据我所知它应该给出相同的结果(它确实是复制粘贴),无论我直接或通过临时表执行…但是如果我在一个语句中执行它,显然会出现问题.
PS:我不需要为此修复,我已经有一个解决方法(见下文),我只是希望有人可以解释我为什么这样做.
解决方法:
SELECT TOP 100 result = SubString('0123456789abcdef',1 + (ABS(Binary_Checksum(NewID())) % 16),1) FROM sys.objects
解决方法
我相信,与
simple CASE expression的描述相反,它实际上为每个input_expression = when_expression比较重新评估input_expression(这通常是安全的,除非在这种情况下,在input_expression中有一个非确定性函数)
所以,会发生的事情是,每次比较时它会在0到15之间产生不同的随机数,如果在16次评估/比较之后,它从未生成匹配的数字,那么就会出现问题.
这不会产生什么:
SELECT TOP 100 result = (CASE ABS(Binary_Checksum(Value)) % 16 WHEN -1 THEN 'hello' WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' WHEN 3 THEN '3' WHEN 4 THEN '4' WHEN 5 THEN '5' WHEN 6 THEN '6' WHEN 7 THEN '7' WHEN 8 THEN '8' WHEN 9 THEN '9' WHEN 10 THEN 'a' WHEN 11 THEN 'b' WHEN 12 THEN 'c' WHEN 13 THEN 'd' WHEN 14 THEN 'e' WHEN 15 THEN 'f' ELSE 'huh' END) FROM (select NewID() as Value,* from sys.objects ) so