T-SQL Case语句使用newid()作为随机源的奇怪行为

Ans*_*sss 7 t-sql sql-server

我正在使用SQL Server 2012.

如果我执行以下操作以获取[1,3]范围内的随机数列表,它可以正常工作:

SELECT TOP 100 
    ABS(CHECKSUM(NEWID()))%3 + 1 [value_of_rand]
FROM sys.objects
Run Code Online (Sandbox Code Playgroud)

我得到了这样的好东西(1到3之间).

3
2
2
2
1
....etc.
Run Code Online (Sandbox Code Playgroud)

但是,如果我然后将相同的chained-random-value函数的输出放入CASE语句中,那么它显然不会仅生成值1,2,3.

SELECT TOP 100 
    CASE (ABS(CHECKSUM(NEWID()))%3 + 1)
        WHEN 1
            THEN 'one'
        WHEN 2
            THEN 'two'
        WHEN 3
            THEN 'three'
        ELSE
            'that is strange'
    END [value_of_case]
FROM sys.objects
Run Code Online (Sandbox Code Playgroud)

它输出:

three
that is strange
that is strange
one
two
...etc
Run Code Online (Sandbox Code Playgroud)

我在这做错了什么?

Luk*_*zda 7

您的:

SELECT TOP 100 
    CASE (ABS(CHECKSUM(NEWID()))%3 + 1)
        WHEN 1
            THEN 'one'
        WHEN 2
            THEN 'two'
        WHEN 3
            THEN 'three'
        ELSE
            'that is strange'
    END [value_of_case]
FROM sys.objects
Run Code Online (Sandbox Code Playgroud)

实际执行:

SELECT TOP 100 
    CASE 
        WHEN (ABS(CHECKSUM(NEWID()))%3 + 1) = 1  THEN 'one'
        WHEN (ABS(CHECKSUM(NEWID()))%3 + 1) = 2  THEN 'two'
        WHEN (ABS(CHECKSUM(NEWID()))%3 + 1) = 3  THEN 'three'
        ELSE 'that is strange'
    END [value_of_case]
FROM sys.objects;
Run Code Online (Sandbox Code Playgroud)

基本上你的表达是非确定性的,每次都被评估,所以你最终可以得到ELSE clause.所以没有错误或捕获,只是你使用变量表达式,这是完全正常的行为.

这是同一类的 COALESCE syntactic-sugar

COALESCE表达式是CASE表达式的语法快捷方式.也就是说,代码COALESCE(expression1,... n)由查询优化器重写为以下CASE表达式:

案件

WHEN(expression1 IS NOT NULL)那么表达式1

WHEN(表达式2非空)然后表达式2

...

ELSE表达式N.

结束

这意味着将多次评估输入值(expression1,expression2,expressionN等).此外,根据SQL标准,包含子查询的值表达式被视为非确定性,子查询将被计算两次.在任何一种情况下,可以在第一次评估和随后的评估之间返回不同的结果.

编辑:

解决方案: SqlFiddle

SELECT TOP 100 
    CASE t.col
        WHEN 1     THEN 'one'
        WHEN 2     THEN 'two'
        WHEN 3     THEN 'three'
        ELSE      'that is strange'
    END [value_of_case]
FROM sys.objects
CROSS APPLY ( SELECT ABS(CHECKSUM(NEWID()))%3 + 1 ) AS t(col)
Run Code Online (Sandbox Code Playgroud)