Нулевые значения в выражении CASE

8

Я играю с некоторыми вещами в SSMS, чтобы узнать немного больше, когда я готовлюсь к экзамену 70-461, и я наткнулся на небольшое зависание. Я пытаюсь создать таблицу, с которой можно поиграться, поэтому мне не нужно изменять / удалять любые из уже созданных таблиц в базах данных AdventureWorks или TSQL2012. Я создал временную таблицу для проверки своего кода перед тем, как создать таблицу для игры, и это код, который я использую для вставки значений в мою таблицу:

DECLARE @i INT = 1
 WHILE @i < 10
    BEGIN
    INSERT INTO #TestEmployeeCountry 
    VALUES ( SUBSTRING('ABCDEFGHIJKLMNOP', @i, 1), 
        CASE (SELECT ABS(CHECKSUM(NEWID()))%10 +1)
            WHEN 1 THEN 'USA'
            WHEN 2 THEN 'CANADA'
            WHEN 3 THEN 'MEXICO'
            WHEN 4 THEN 'UK'
            WHEN 5 THEN 'FRANCE'
            WHEN 6 THEN 'SPAIN'
            WHEN 7 THEN 'RUSSIA'
            WHEN 8 THEN 'CHINA'
            WHEN 9 THEN 'JAPAN'
            WHEN 10 THEN 'INDIA'
        END)
    SET @i = @i + 1
    END;

Проблема, с которой я сталкиваюсь, заключается в том, что я получаю сообщение об ошибке: «Невозможно вставить значение NULL в столбец« Страна », таблица« tempdb.dbo. # TestEmployeeCountry ». Причина этого в том, что для столбца Страна установлено значение NOT NULL» мой код работает для некоторых вставок, проблема в том, что я случайно получаю значения NULL из моего оператора case.

Я знаю, что для исправления этого я могу легко добавить еще одну строку с надписью «ПО УМОЛЧАНИЮ xxxxxx», однако я хочу понять, что происходит, потому что исходя из того, что я вижу, мне не нужно этого делать, не так ли? Мне показалось, что я правильно написал свое утверждение с указанием числа от 1 до 10, и после тестирования только этого конкретного оператора выбора за 1000 попыток я всегда получаю случайное число от 1 до 10, ничего больше или меньше. Может кто-нибудь помочь мне понять, почему этот код пытается ввести значение NULL в этот столбец?

user2921015
источник

Ответы:

8

Почему это происходит, @PaulWhite уже ответил на вопрос SO: Как это выражение CASE достигает предложения ELSE?

Чтобы решить эту проблему, вы должны вычислить ABS(CHECKSUM(NEWID()))%10 +1внешнее / перед INSERTоператором, чтобы он вычислялся один раз. Что-то вроде:

DECLARE @i INT = 1 ;
DECLARE @rand INT ;
 WHILE @i <= 10
   BEGIN
    SET @rand = ABS(CHECKSUM(NEWID()))%10 +1 ;
    INSERT INTO TestEmployeeCountry 
    VALUES ( SUBSTRING('ABCDEFGHIJKLMNOP', @i, 1), 
        CASE @rand
            WHEN 1 THEN 'USA'
            WHEN 2 THEN 'CANADA'
            WHEN 3 THEN 'MEXICO'
            WHEN 4 THEN 'UK'
            WHEN 5 THEN 'FRANCE'
            WHEN 6 THEN 'SPAIN'
            WHEN 7 THEN 'RUSSIA'
            WHEN 8 THEN 'CHINA'
            WHEN 9 THEN 'JAPAN'
            WHEN 10 THEN 'INDIA'
        END) ;
    SET @i = @i + 1 ;
   END ;

Также обратите внимание, что с вашим кодом 10 стран не будут размещены в таблице с равной вероятностью! Первая страна ( USA) будет иметь шанс 10%, вторая будет иметь 9% ( (100%-10%)*10%), третья - 8,1%, ( (100%-19%)*10%) и т. Д. Это оставляет не такой маленький шанс (около 1/e), что ни один из 10 не выбран, и CASEвыражение идет по умолчанию, ELSE NULLи вы получите ошибку. (Вы можете проверить вероятности, если разрешите пустые значения в столбце и запустите сценарий SQLfiddle .)

В соответствии с вышеизложенным, другим способом решения этой проблемы было бы изменение выражений в соответствии с тем, как SQL-Server выполняет, CASEи все 10 случаев имеют одинаковую вероятность:

    CASE 0
        WHEN ABS(CHECKSUM(NEWID()))%10 THEN 'USA'
        WHEN ABS(CHECKSUM(NEWID()))%9 THEN 'CANADA'
        WHEN ABS(CHECKSUM(NEWID()))%8 THEN 'MEXICO'
        WHEN ABS(CHECKSUM(NEWID()))%7 THEN 'UK'
        WHEN ABS(CHECKSUM(NEWID()))%6 THEN 'FRANCE'
        WHEN ABS(CHECKSUM(NEWID()))%5 THEN 'SPAIN'
        WHEN ABS(CHECKSUM(NEWID()))%4 THEN 'RUSSIA'
        WHEN ABS(CHECKSUM(NEWID()))%3 THEN 'CHINA'
        WHEN ABS(CHECKSUM(NEWID()))%2 THEN 'JAPAN'
        ELSE 'INDIA'
    END
ypercubeᵀᴹ
источник