Valeurs nulles dans une instruction CASE

8

Je joue avec des trucs dans SSMS pour en savoir un peu plus en étudiant pour mon examen 70-461 et je suis tombé sur un petit blocage. J'essaie de créer une table pour jouer avec, donc je n'ai pas à modifier / supprimer l'une des tables déjà créées dans les bases de données AdventureWorks ou TSQL2012. J'ai créé une table temporaire pour tester mon code avant de créer une table pour jouer avec et c'est le code que j'utilise pour insérer des valeurs dans ma table:

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;

Le problème que je rencontre est que je reçois toujours une erreur disant "Impossible d'insérer la valeur NULL dans la colonne 'Country', la table 'tempdb.dbo. # TestEmployeeCountry" La raison en est que j'ai la colonne Country définie sur NOT NULL, et mon code fonctionne pour certaines des insertions, le problème est que j'obtiens au hasard des valeurs NULL de ma déclaration de cas.

Je sais que pour résoudre ce problème, je peux facilement ajouter une autre ligne qui dit "DEFAUT xxxxxx" mais je veux comprendre ce qui se passe parce que d'après ce que je vois, je ne devrais pas avoir à le faire, non? Je pensais avoir écrit ma déclaration de cas correctement, en me donnant un nombre compris entre 1 et 10 uniquement et en testant uniquement cette déclaration spécifique spécifique sur 1000 essais, j'obtiens toujours un nombre aléatoire entre 1 et 10, rien de plus grand ou de plus petit. Quelqu'un peut-il m'aider à comprendre pourquoi ce code essaie d'entrer une valeur NULL dans cette colonne?

user2921015
la source

Réponses:

8

@PaulWhite a déjà répondu à la question pourquoi: comment cette expression CASE atteint-elle la clause ELSE?

Pour le résoudre, vous devez calculer l' ABS(CHECKSUM(NEWID()))%10 +1extérieur / avant l' INSERTinstruction afin qu'elle soit calculée une fois. Quelque chose comme:

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 ;

Notez également qu'avec votre code, les 10 pays ne seront pas placés dans le tableau avec une probabilité égale! Le premier pays ( USA) aura 10% de chances, le second aura 9% ( (100%-10%)*10%), le troisième de 8,1%, ( (100%-19%)*10%), etc. Cela laisse pas si petite chance (autour 1/equ'aucun des 10 est choisi) et l' CASEexpression passe à la valeur par défaut ELSE NULLet vous obtenez l'erreur. (Vous pouvez vérifier les probabilités si vous autorisez les valeurs NULL dans la colonne et exécutez le script SQLfiddle .)

Selon ce qui précède, une autre façon de le résoudre serait de modifier les expressions pour se conformer à la façon dont SQL-Server exécute les CASE10 cas et les 10 ont la même probabilité:

    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ᵀᴹ
la source