CASE语句中随机生成的值返回NULL

时间:2015-05-05 13:24:22

标签: sql-server tsql random case

根据this post,在SQL Server中生成随机值的正确方法是:

ABS(CHECKSUM(NewId())) % 14   -- Returns a value between 0 and 13

但是,在case语句中使用此表达式时,例如:

SELECT
    CASE ABS(CHECKSUM(NEWID())) % 4 
        WHEN 0 THEN 'String A' 
        WHEN 1 THEN 'String B' 
        WHEN 2 THEN 'String C' 
        WHEN 3 THEN 'String D'
    END AS RandomString -- Returns String A, B, C, D and NULLs.
FROM sys.all_objects

outputtet RandomString列包含一些NULL,如此SQL fiddle中所示。我发现我可以在CTE中包装随机化表达式以避免输出中的NULL,但我仍然想知道为什么上面的代码返回NULL?

WITH RandomNumber AS (
    SELECT ABS(CHECKSUM(NEWID())) % 4 AS N FROM sys.all_objects
)
SELECT TOP 100
    CASE N
        WHEN 0 THEN 'String A' 
        WHEN 1 THEN 'String B' 
        WHEN 2 THEN 'String C' 
        WHEN 3 THEN 'String D'
    END AS RandomString -- Does not return any NULLs. Only String A, B, C and D.
FROM RandomNumber

我尝试使用稍微不同的方法生成随机数,但结果是相同的:

CAST(RAND(CHECKSUM(NEWID())) * 4 AS INT)  -- Returns a value between 0 and 3

这似乎是SQL Server 2014上的问题,我还没有在其他版本上测试过。

4 个答案:

答案 0 :(得分:6)

它会生成NULL,因为无法保证评估特定表达式的次数。

您希望SQL Server能够执行以下操作:

let x = GenerateRandomNumber()
if x = 1 then 'String 1'
if x = 2 then 'String 2'
if x = 3 then 'String 3'
if x = 4 then 'String 4'

GenerateRandomNumber()ABS(CHECKSUM(NEWID())) % 4);但SQL Server实际上做的是:

if GenerateRandomNumber() = 1 then 'String 1'
if GenerateRandomNumber() = 2 then 'String 2'
if GenerateRandomNumber() = 3 then 'String 3'
if GenerateRandomNumber() = 4 then 'String 4'

因此,如果您碰巧为一个特定的比较操作选择了正确的随机数,那么您只会获得非NULL结果。

我认为即使使用CTE,也没有保证 SQL Server不会生成类似上面第二个代码块的东西。如果你想要一个 stable ,生成一次随机数,你需要在某处安排 store 该值(例如在表变量或临时表中)。

我专注于保证的原因是您不希望最终根据当前观察到的行为编写代码。当SQL Server 2008在视图中停止“排序”结果我们正在使用TOP 100 PERCENT ... ORDER BY技巧时,报告了很多“问题” - 这些事情恰好在2005年及早期版本中发生(但大部分时间),但已停止执行如此。

同样,如果有人要求我提供一个返回数字5的表达式,我可以为它们提供表达式DATEPART(day,GETUTCDATE())并让它们在尽可能多的行上运行尽可能多的查询 - 对于接下来的8个小时 - 但这并不意味着我建议将其作为解决问题的方法。

此外,我们知道SQL Server有关evaluation order的决定可能会令人惊讶。

答案 1 :(得分:6)

表达式在内部被重写为与

相同的逻辑
CASE WHEN ABS(CHECKSUM(NEWID())) % 4 = 1 THEN x 
     WHEN ABS(CHECKSUM(NEWID())) % 4 = 2 THEN y 
     WHEN ...

这就是为什么问题中的语法永远不会起作用的原因。 正在评估每个WHEN的表达式。

你可以解决的方法是:

SELECT 
    CASE x % 4 
        WHEN 0 THEN 'String A' 
        WHEN 1 THEN 'String B' 
        WHEN 2 THEN 'String C' 
        WHEN 3 THEN 'String D'
    END AS RandomString -- Returns String A, B, C, D and NULLs.
FROM sys.all_objects
CROSS APPLY (SELECT ABS(CHECKSUM(NEWID())) x) y

使用CROSS APPLY只会计算一次

答案 2 :(得分:0)

就目前而言,我不太确定。但就你的null问题而言,将其作为派生表并查询是否有效。不知道为什么。

import os
subpath ="filedir/filename.txt"
print os.path.join(os.getenv("LOCALAPPDATA"), subpath)

答案 3 :(得分:0)

这是对随机数和空值的深思熟虑的讨论。我在这个问题上看到的最佳答案和解释来自Aaron Bertrand的文章"Dirty Secrets of the CASE Expression,",该文章发表于sqlperformance.com。在我的情况下,我将查询D CROSS APPLY合并,以获得我想要的结果。

enter image description here