Странное поведение конструкции CASE - PullRequest
8 голосов
/ 15 декабря 2011

Справочная информация: я пытался получить случайные «шестнадцатеричные» значения при создании фиктивных данных и придумал такую ​​конструкцию:

SELECT TOP 100 
   result = (CASE ABS(Binary_Checksum(NewID())) % 16 
                WHEN -1 THEN 'hello'
                WHEN 0 THEN '0' 
                WHEN 1 THEN '1' 
                WHEN 2 THEN '2' 
                WHEN 3 THEN '3'
                WHEN 4 THEN '4' 
                WHEN 5 THEN '5' 
                WHEN 6 THEN '6' 
                WHEN 7 THEN '7'
                WHEN 8 THEN '8' 
                WHEN 9 THEN '9' 
                WHEN 10 THEN 'a' 
                WHEN 11 THEN 'b'
                WHEN 12 THEN 'c' 
                WHEN 13 THEN 'd' 
                WHEN 14 THEN 'e' 
                WHEN 15 THEN 'f' 
                ELSE 'huh'  END)
          FROM sys.objects 

При запуске этого на моем экземпляре SQL Server 2008 R2 я получаю довольномного «да» записей:

result
------
huh
3
huh
huh
6
8
6

Я действительно не понимаю, почему.Я ожидаю, что произойдет следующее:

  • для каждой записи NewID() получит новое случайное значение
  • Binary_Checksum() вычисляет целое число на основеуказанное значение
  • ABS() делает значение положительным
  • % 16 возвращает остаток от этого положительного значения, если оно будет разделено на 16, которое затем будет значением от 0 до 15
  • конструкция CASE преобразует значение в соответствующий символ
  • Поскольку для каждого значения от 0 до 15 существует WHEN s, ELSE никогда не требуется

или, по крайней мере, это то, что я думаю, должно произойти ... но, очевидно, что-то идет не так по дороге ...

При выполнении того же самого в двухступенчатом подходе (черезвременная таблица), да ушли ...

SELECT TOP 100 x = ABS(Binary_Checksum(NewID())) % 16,
               result = 'hello'
  INTO #test
  FROM sys.objects

UPDATE #test 
   SET result = (CASE x WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' WHEN 3 THEN '3'
                        WHEN 4 THEN '4' WHEN 5 THEN '5' WHEN 6 THEN '6' WHEN 7 THEN '7'
                        WHEN 8 THEN '8' WHEN 9 THEN '9' WHEN 10 THEN 'a' WHEN 11 THEN 'b'
                        WHEN 12 THEN 'c' WHEN 13 THEN 'd' WHEN 14 THEN 'e' WHEN 15 THEN 'f' 
                        ELSE 'huh'  END)

SELECT * FROM #test

Кто-нибудь, кто понимает это?Насколько я могу судить, он должен давать один и тот же результат (это действительно копирование-вставка) независимо от того, выполняю ли я это напрямую или через временную таблицу ... Но, очевидно, что-то идет не так, если я делаю это в одном выражении.

PS: мне не нужно «исправлять» для этого, у меня уже есть обходной путь (см. Ниже), я просто надеюсь, что кто-то может объяснить мне, почему это делает то, что он делает.

Обходной путь:

SELECT TOP 100 result = SubString('0123456789abcdef', 1 + (ABS(Binary_Checksum(NewID())) % 16), 1) 
  FROM sys.objects

Ответы [ 2 ]

8 голосов
/ 15 декабря 2011

Расчетный скаляр в плане имеет следующую формулу

[Expr1038] = Скалярный оператор (СЛУЧАЙ, КОГДА abs (binary_checksum (newid ()))% (16) = (- 1) THEN 'Здравствуйте', ELSE CASE WHEN WHEN abs (binary_checksum (newid ()))% (16) = (0) THEN '0' ELSE CASE WHEN abs (binary_checksum (newid ()))% (16) = (1) THEN '1' ELSE CASE WHEN abs (binary_checksum (newid ()))% (16) = (2) THEN '2' ELASE CASE WHEN WHEN abs (binary_checksum (newid ()))% (16) = (3) THEN '3' ELSE CASE WHEN abs (binary_checksum (newid ()))% (16) = (4) THEN '4' ELSE CASE WHEN abs (binary_checksum (newid ()))% (16) = (5) ТОГДА '5' ИЛИ ​​СЛУЧАЙ, КОГДА abs (binary_checksum (newid ()))% (16) = (6) THEN '6' ELSE CASE WHEN WHEN abs (binary_checksum (newid ()))% (16) = (7) THEN '7' ELASE CASE WHEN WHEN abs (binary_checksum (newid ()))% (16) = (8), ТОГДА '8', ДАЖЕ, КОГДА abs (binary_checksum (newid ()))% (16) = (9) THEN '9' ELSE CASE WHEN abs (binary_checksum (newid ()))% (16) = (10) THEN 'a' ДАЖЕ ПРИ СЛУЧАЕ abs (binary_checksum (newid ()))% (16) = (11) ТОГДА 'b' ДАЖЕ ПРИМЕР КОГДА abs (binary_checksum (newid ()))% (16) = (12) THEN 'c' EASE CASE WHEN WHEN abs (binary_checksum (newid ()))% (16) = (13) ТОЛЬКО СЛУЧАЙ, КОГДА КОГДА abs (binary_checksum (newid ()))% (16) = (14) THEN 'e' EASE CASE WHEN WHEN abs (binary_checksum (newid ()))% (16) = (15) THEN 'f' ELSE 'huh' END END END END END END END END END END END END END END END END)

Случайное число повторно оценивается, а не оценивается один раз, и сохраняется постоянным на протяжении каждой ветви оператора CASE.

(фиксированное) предлагаемое решение в ответе Дэмиена действительно работает для меня

SELECT TOP 100 
    result = (CASE ABS(Binary_Checksum(Value)) % 16 
            WHEN -1 THEN 'hello'
            /*...*/
            ELSE 'huh'  END)
          FROM (select NewID() as Value,* from sys.objects ) so

Потому что в плане 2 вычислительных скалярных оператора. Первый с определением

[Expr1038] = Scalar Operator(newid())

Plan

Затем это постоянное выражение Expr1038 вводится в выражение CASE. Однако я не уверен, что такое поведение абсолютно гарантировано. Это может зависеть от прихоти оптимизатора.

3 голосов
/ 15 декабря 2011

Я считаю, что вопреки описанию простого выражения CASE , что оно фактически переоценивает input_expression для каждого input_expression = when_expression сравнения (это обычно было бы безопасно, если, как в этомВ этом случае есть недетерминированная функция в input_expression)

Итак, что происходит, так это то, что она продолжает генерировать разные случайные числа от 0 до 15 для каждого сравнения, и huh s появляется, если после16 оценок / сравнений, оно никогда не генерировало совпадающее число.


Это не генерирует huh s:

SELECT TOP 100 
    result = (CASE ABS(Binary_Checksum(Value)) % 16 
            WHEN -1 THEN 'hello'
            WHEN 0 THEN '0' 
            WHEN 1 THEN '1' 
            WHEN 2 THEN '2' 
            WHEN 3 THEN '3'
            WHEN 4 THEN '4' 
            WHEN 5 THEN '5' 
            WHEN 6 THEN '6' 
            WHEN 7 THEN '7'
            WHEN 8 THEN '8' 
            WHEN 9 THEN '9' 
            WHEN 10 THEN 'a' 
            WHEN 11 THEN 'b'
            WHEN 12 THEN 'c' 
            WHEN 13 THEN 'd' 
            WHEN 14 THEN 'e' 
            WHEN 15 THEN 'f' 
            ELSE 'huh'  END)
          FROM (select NewID() as Value,* from sys.objects ) so
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...