WITH SillySequence (Ordinal) AS (
SELECT 1
UNION ALL
SELECT (1 + Ordinal) FROM SillySequence WHERE Ordinal < 100
)
SELECT
NeverNull =
CHOOSE(
RandomValue,
'one', 'two'),
SometimesNull =
CHOOSE(
1 + ABS(CHECKSUM(NEWID()) % 2),
'one', 'two')
FROM (
SELECT RandomValue = 1 + ABS(CHECKSUM(NEWID()) % 2)
FROM SillySequence
) _
以下是几点说明:
SillySequence
仅用于生成100条记录。1 + ABS(CHECKSUM(NEWID()) % 2)
产生(随机)1或2。NeverNull
嗯,永远不会NULL
。 NeverNull
和SometimesNull
之间的唯一区别是NeverNull
的{{1}}函数的第一个参数是在单独的子查询中计算的。CHOOSE
时,永远不会生成NULL
个值。要产生% 1
,除数必须为2或更大。以下示例随机选择5,6和NULL
。它永远不会选择任何其他值,这使我相信NULL
在直接作为ABS(CHECKSUM(NEWID()) % 2)
的参数提供时偶尔会产生NULL
。
CHOOSE
有趣的是,我总是可以通过以下方式触发“严重”错误:
WITH SillySequence (Ordinal) AS (
SELECT 1
UNION ALL
SELECT (1 + Ordinal) FROM SillySequence WHERE Ordinal < 100
)
SELECT
SometimesNull =
CHOOSE(
5 + ABS(CHECKSUM(NEWID()) % 2),
1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
FROM SillySequence
Msg 0,Level 11,State 0,Line 20
无法继续执行,因为会话处于终止状态。
Msg 0,Level 20,State 0,Line 20
当前命令发生严重错误。结果(如果有的话)应该被丢弃。
我使用兼容级别SELECT CHOOSE(
(SELECT 1 + ABS(CHECKSUM(NEWID()) % 2)),
'one', 'two')
,110
和120
在SQL Server 2016中测试了上述示例。
答案 0 :(得分:1)
你误解了choose()
的工作原理。在我看来,这是因为你是一个聪明的人,期望软件以合理的方式工作。
choose(x, a, b)
的定义是它等同于:
(case when x = 1 then a
when x = 2 then b
end)
当x
为常数时,就够了。当x
引用一列时(例如在“never null”示例中),这很简单。当x
是非易失性表达式时足够简单。当x
是非确定性表达时,99.9%的反直觉(有人可能会有不同的想法和评论;)
这是什么意思?好吧,你正在执行:
(case when 1 + ABS(CHECKSUM(NEWID()) % 2) = 1 then a
when 1 + ABS(CHECKSUM(NEWID()) % 2) = 2 then b
end)
也就是说,表达式有两个随机数。事实上,每个可能目标的一个随机数(我确实说“反直觉”,不是吗?)。
嗯,大约25%的时间,两次比较都会失败。而且,结果将是NULL
。我记得在我第一次学习这个时花了很多时间调试代码。