SQL的CHOOSE选择了错误的结果(产生“严重”错误)

时间:2017-12-19 19:26:51

标签: sql-server tsql

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嗯,永远不会NULLNeverNullSometimesNull之间的唯一区别是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') 110120在SQL Server 2016中测试了上述示例。

1 个答案:

答案 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。我记得在我第一次学习这个时花了很多时间调试代码。