CASE结构的奇怪行为

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() 根据所述值计算int
  • ABS() 使价值积极
  • % 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)

Mar*_*ith 8

计划中的计算标量具有以下公式

[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表达式.我不确定这种行为是绝对保证的.它可能受优化者的一时兴起.

  • @deroby - 我在答案中添加了一些内容.它似乎**工作,但我怀疑你会发现任何铸铁保证它将永远有效.SQL Server通常保留在树中重新排序表达式的权利[正如本答案中所解释的那样](http://stackoverflow.com/questions/5191701/tsql-divide-by-zero-encountered-despite含-no柱-0/5203211#5203211) (3认同)
  • @deroby - 遗憾的是,您无法使用计划指南来强制计算标量.XML计划中的任何计算标量都会被忽略,您最终可能会得到不同的计划. (2认同)

Dam*_*ver 3

我相信,与简单 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)

  • 那应该是:`结果 = (CASE ABS(Binary_Checksum(Value)) % 16` (2认同)