CASE 语句中随机生成的值 return NULL
Randomly generated values in CASE statements return NULL
根据this post,在SQL服务器中生成随机值的正确方法是:
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,但我仍然想知道为什么上面的代码 returns NULLs?
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 上的问题,我没有在其他版本上测试过。
它生成 NULL
s 因为不能保证特定表达式将被计算多少次。
您想要的是 SQL 服务器按照以下方式执行某些操作:
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 服务器实际做的是:
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 服务器永远不会生成类似上面第二个代码块的内容。如果你想要一个 stable,生成一次随机数,你需要安排 store 该值在某处(例如在 table 变量或温度 table).
我关注 保证 的原因是您不希望最终根据当前观察到的行为编写代码。当 SQL Server 2008 停止时,有很多 "issues" 报告 "ordering" 结果是我们正在使用 TOP 100 PERCENT ... ORDER BY
技巧的视图 - 碰巧(大部分)在2005 及更早版本但已停止这样做。
类似地,如果有人问我 returns 数字 5
的表达式,我可以为他们提供表达式 DATEPART(day,GETUTCDATE())
并让他们 运行 尽可能多的查询在接下来的 8 小时内,他们喜欢多少行都可以,但这并不意味着我会推荐它作为解决他们问题的方法。
而且,我们知道在某些方面 SQL 服务器关于 evaluation order 的决定可能令人惊讶。
表达式正在内部重写为与
相同的逻辑
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只会计算一次
至于为什么,我不太确定。但就您的空问题而言,将其设为派生 table 并进行查询。真的不知道为什么。
SELECT CASE rand_num
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
(
SELECT ABS(CHECKSUM(NEWID())) % 4 rand_num
FROM sys.all_objects
) A
WHERE rand_num BETWEEN 0 AND 3
这是关于随机数和空值的深思熟虑的讨论。我在这个问题上看到的最佳答案和解释来自 Aaron Bertrand 的文章 "Dirty Secrets of the CASE Expression,",该文章发表于 sqlperformance.com。在我的情况下,我将 Query D 与 CROSS APPLY
结合起来以获得我正在寻找的结果。
根据this post,在SQL服务器中生成随机值的正确方法是:
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,但我仍然想知道为什么上面的代码 returns NULLs?
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 上的问题,我没有在其他版本上测试过。
它生成 NULL
s 因为不能保证特定表达式将被计算多少次。
您想要的是 SQL 服务器按照以下方式执行某些操作:
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 服务器实际做的是:
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 服务器永远不会生成类似上面第二个代码块的内容。如果你想要一个 stable,生成一次随机数,你需要安排 store 该值在某处(例如在 table 变量或温度 table).
我关注 保证 的原因是您不希望最终根据当前观察到的行为编写代码。当 SQL Server 2008 停止时,有很多 "issues" 报告 "ordering" 结果是我们正在使用 TOP 100 PERCENT ... ORDER BY
技巧的视图 - 碰巧(大部分)在2005 及更早版本但已停止这样做。
类似地,如果有人问我 returns 数字 5
的表达式,我可以为他们提供表达式 DATEPART(day,GETUTCDATE())
并让他们 运行 尽可能多的查询在接下来的 8 小时内,他们喜欢多少行都可以,但这并不意味着我会推荐它作为解决他们问题的方法。
而且,我们知道在某些方面 SQL 服务器关于 evaluation order 的决定可能令人惊讶。
表达式正在内部重写为与
相同的逻辑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只会计算一次
至于为什么,我不太确定。但就您的空问题而言,将其设为派生 table 并进行查询。真的不知道为什么。
SELECT CASE rand_num
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
(
SELECT ABS(CHECKSUM(NEWID())) % 4 rand_num
FROM sys.all_objects
) A
WHERE rand_num BETWEEN 0 AND 3
这是关于随机数和空值的深思熟虑的讨论。我在这个问题上看到的最佳答案和解释来自 Aaron Bertrand 的文章 "Dirty Secrets of the CASE Expression,",该文章发表于 sqlperformance.com。在我的情况下,我将 Query D 与 CROSS APPLY
结合起来以获得我正在寻找的结果。