Valores nulos em uma instrução CASE

8

Estou brincando com algumas coisas no SSMS para aprender um pouco mais enquanto estudo para o exame 70-461 e me deparei com um pouco de dificuldade. Estou tentando criar uma tabela para brincar, para não precisar alterar / excluir nenhuma das tabelas já criadas nos bancos de dados AdventureWorks ou TSQL2012. Eu criei uma tabela temporária para testar meu código antes de criar uma tabela para brincar e este é o código que estou usando para inserir valores na minha tabela:

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;

O problema que estou tendo é que continuo recebendo um erro dizendo "Não é possível inserir o valor NULL na coluna 'País', tabela 'tempdb.dbo. # TestEmployeeCountry" O motivo é que eu tenho a coluna País definida como NOT NULL e meu código funciona para algumas inserções, o problema é que eu recebo aleatoriamente valores NULL da minha declaração de caso.

Eu sei que, para corrigir isso, posso facilmente adicionar outra linha que diz "PADRÃO xxxxxx", no entanto, quero entender o que está acontecendo, porque, com base no que vejo, não devo fazer isso, devo? Eu pensei que escrevi minha declaração de caso corretamente, fornecendo um número entre 1 e 10 e, após testar apenas essa declaração específica em mais de 1000 tentativas, sempre recebo um número aleatório entre 1 e 10, nada maior ou menor. Alguém pode me ajudar a entender por que esse código tenta inserir um valor NULL nessa coluna?

user2921015
fonte

Respostas:

8

Por que isso acontece já foi respondido por @PaulWhite na pergunta SO: Como essa expressão CASE atinge a cláusula ELSE?

Para resolvê-lo, você deve calcular ABS(CHECKSUM(NEWID()))%10 +1a INSERTdeclaração externa / anterior para que seja calculada uma vez. Algo como:

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 ;

Observe também que, com o seu código, os 10 países não serão colocados na tabela com igual probabilidade! O primeiro país ( USA) terá 10% de chance, o segundo terá 9% ( (100%-10%)*10%), o terceiro 8,1%, ( (100%-19%)*10%), etc. Isso deixa uma chance não tão pequena (em torno de 1/e) que nenhum dos 10 seja escolhido e a CASEexpressão vai para o padrão ELSE NULLe você recebe o erro. (Você pode verificar as probabilidades se permitir nulos na coluna e executar o script SQLfiddle .)

De acordo com o exposto acima, outra maneira de resolvê-lo seria alterar as expressões para obedecer à maneira como o SQL-Server executa os CASEe todos os 10 casos têm a mesma probabilidade:

    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ᵀᴹ
fonte