Valori null in un'istruzione CASE


8

Sto giocando con alcune cose in SSMS per imparare un po 'di più mentre studio per il mio esame 70-461 e mi sono imbattuto in un po' di problemi. Sto cercando di creare un tavolo con cui giocare, quindi non devo modificare / eliminare nessuno dei tavoli già creati nei database AdventureWorks o TSQL2012. Ho creato una tabella temporanea per testare il mio codice prima di creare effettivamente una tabella con cui giocare e questo è il codice che sto usando per inserire valori nella mia tabella:

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;

Il problema che sto riscontrando è che continuo a ricevere un errore che dice "Impossibile inserire il valore NULL nella colonna" Paese ", tabella" tempdb.dbo. # TestEmployeeCountry "Il motivo è perché la colonna Paese è impostata su NOT NULL e il mio codice funziona per alcuni degli inserti, il problema è che estraggo casualmente i valori NULL dall'istruzione case.

So che per risolvere questo problema posso facilmente aggiungere un'altra riga che dice "DEFAULT xxxxxx", tuttavia voglio capire cosa sta succedendo perché, in base a ciò che vedo, non dovrei farlo, dovrei? Ho pensato di aver scritto correttamente la mia dichiarazione del caso, dandomi un numero compreso tra 1 e 10 e dopo aver testato solo quella specifica istruzione selezionata per oltre 1000 tentativi, ottengo sempre un numero casuale compreso tra 1 e 10, niente di più grande o più piccolo. Qualcuno può aiutarmi a capire perché questo codice tenta di inserire un valore NULL in quella colonna?

Risposte:


8

Perché questo accade è già stato risposto da @PaulWhite nella domanda SO: In che modo questa espressione CASE raggiunge la clausola ELSE?

Per risolverlo, è necessario calcolare l' ABS(CHECKSUM(NEWID()))%10 +1esterno / prima INSERTdell'istruzione in modo che venga calcolata una volta. Qualcosa di simile a:

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 ;

Nota anche che con il tuo codice, i 10 paesi non verranno inseriti nella tabella con uguale probabilità! Il primo paese ( USA) avrà una probabilità del 10%, il secondo avrà il 9% ( (100%-10%)*10%), il terzo 8,1%, ( (100%-19%)*10%), ecc. Ciò lascia una probabilità non così piccola (circa 1/e) che nessuno dei 10 sia scelto e l' CASEespressione passa all'impostazione predefinita ELSE NULLe viene visualizzato l'errore. (Puoi verificare le probabilità se consenti null nella colonna ed esegui lo script SQLfiddle .)

In base a quanto sopra, un altro modo per risolverlo sarebbe quello di cambiare le espressioni per adeguarsi a come SQL Server esegue CASEe tutti e 10 i casi hanno la stessa 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.