der*_*oby 8 t-sql sql-server sql-server-2008
背景:我试图在创建虚拟数据时获得一些随机的"十六进制"值,并提出了这种结构:
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
Run Code Online (Sandbox Code Playgroud)
在我的SQL Server 2008 R2实例上运行时,我收到了很多'huh'记录:
result
------
huh
3
huh
huh
6
8
6
Run Code Online (Sandbox Code Playgroud)
我真的不明白为什么.我期望发生的是:
NewID()都会出现一个新的随机值Binary_Checksum() 根据所述值计算intABS() 使价值积极% 16 如果它除以16,则返回该正值的余数,然后该值将是0到15之间的值CASE建筑的价值转换为相关的字符WHEN0到15之间的每个值都有s,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
Run Code Online (Sandbox Code Playgroud)
有谁理解这个?据我所知它应该给出相同的结果(它确实是复制粘贴),不管我直接或通过临时表进行...但是如果我在一个语句中这样做,显然会出现问题.
PS:我不需要对此进行"修复",我已经有了一个解决方法(见下文),我只是希望有人可以解释我为什么这样做.
解决方法:
SELECT TOP 100 result = SubString('0123456789abcdef', 1 + (ABS(Binary_Checksum(NewID())) % 16), 1)
FROM sys.objects
Run Code Online (Sandbox Code Playgroud)
计划中的计算标量具有以下公式
[Expr1038] =标量运算符(CASE WHEN abs(binary_checksum(newid()))%(16)=( - 1)THEN'hello'ELSE CASE WHEN abs(binary_checksum(newid()))%(16)=(0 )那么'0'是的情况当abs(binary_checksum(newid()))%(16)=(1)那么'1'是的情况当abs(binary_checksum(newid()))%(16)=(2)那么'2'ELSE CASE WHEN abs(binary_checksum(newid()))%(16)=(3)THEN'3'ELSE CASE WHEN(binary_checksum(newid()))%(16)=(4)那么'4 'ELSE CASE WHEN abs(binary_checksum(newid()))%(16)=(5)那么'5'ELSE情况当abs(binary_checksum(newid()))%(16)=(6)那么'6'ELSE例如,当abs(binary_checksum(newid()))%(16)=(7)那么'7'ELSE情况当abs(binary_checksum(newid()))%(16)=(8)那么'8'过的情况下abs(binary_checksum(newid()))%(16)=(9)那么'9'是的情况当abs(binary_checksum(newid()))%(16)=(10)那么'a'是的情况下abs( binary_checksum(newid()))%(16)=(11)那么'b'ELSE情况当abs(binary_checksum(newid()))%(16)=(12)那么'c'ELSE CASE WHEN abs(binary_checksum( newid()))%(16)=(13)那么'当'时的情况(腹部 ary_checksum(newid()))%(16)=(14)那么'e'ELSE情况当abs(binary_checksum(newid()))%(16)=(15)那么'f'ELSE'huh'END END END END END END END END END END END END END END END END END END END)
随机数被重复评估而不是被评估一次并且在CASE语句的每个分支中保持不变.
Damien的答案中提出的(固定的)解决方案对我有用
SELECT TOP 100
result = (CASE ABS(Binary_Checksum(Value)) % 16
WHEN -1 THEN 'hello'
/*...*/
ELSE 'huh' END)
FROM (select NewID() as Value,* from sys.objects ) so
Run Code Online (Sandbox Code Playgroud)
因为该计划有2个计算标量运算符.第一个有定义
[Expr1038] = Scalar Operator(newid())
Run Code Online (Sandbox Code Playgroud)

然后将该常量表达式Expr1038输入CASE表达式.我不确定这种行为是绝对保证的.它可能受优化者的一时兴起.
我相信,与简单 CASE 表达式的描述相反,它实际上会重新评估input_expression每次input_expression = when_expression比较(这通常是安全的,除非在本例中,有一个非确定性函数input_expression)
因此,会发生的情况是,它会为每次比较不断生成 0 到 15 之间的不同随机数,并且huh如果在 16 次评估/比较之后,它从未生成匹配的数字,则 s 就会出现。
这不会生成huhs:
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
Run Code Online (Sandbox Code Playgroud)