Valores nulos en una declaración CASE

8

Estoy jugando con algunas cosas en SSMS para aprender un poco más mientras estudio para mi examen 70-461 y me encontré con un pequeño problema. Estoy tratando de crear una tabla para jugar, así que no tengo que alterar / eliminar ninguna de las tablas ya creadas en las bases de datos AdventureWorks o TSQL2012. He creado una tabla temporal para probar mi código antes de crear una tabla para jugar y este es el código que estoy usando para insertar valores en mi tabla:

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;

El problema que tengo es que sigo recibiendo un error que dice "No se puede insertar el valor NULL en la columna 'Country', tabla 'tempdb.dbo. # TestEmployeeCountry" La razón es porque tengo la columna Country establecida en NOT NULL, y mi código funciona para algunas de las inserciones, el problema es que al azar obtengo valores NULL de mi declaración de caso.

Sé que para solucionar esto, puedo agregar fácilmente otra línea que dice "DEFAULT xxxxxx". Sin embargo, quiero entender lo que está sucediendo porque, según lo que veo, no debería tener que hacer eso, ¿verdad? Pensé que escribí mi declaración de caso correctamente, dándome un número entre 1-10 solamente y al probar solo esa declaración de selección específica en más de 1000 intentos, siempre obtengo un número aleatorio entre 1-10, nada más grande o más pequeño. ¿Alguien puede ayudarme a entender por qué este código intenta ingresar un valor NULO en esa columna?

usuario2921015
fuente

Respuestas:

8

@PaulWhite ya respondió a por qué sucede esto en la pregunta SO: ¿Cómo llega esta expresión CASE a la cláusula ELSE?

Para resolverlo, debe calcular el ABS(CHECKSUM(NEWID()))%10 +1exterior / antes de la INSERTdeclaración para que se calcule una 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 ;

¡También tenga en cuenta que con su código, los 10 países no se colocarán en la tabla con la misma probabilidad! El primer país ( USA) tendrá una probabilidad del 10%, el segundo tendrá un 9% ( (100%-10%)*10%), el tercer 8.1%, ( (100%-19%)*10%), etc. Eso deja una posibilidad no tan pequeña (alrededor 1/e) de que ninguno de los 10 sea elegido y la CASEexpresión pasa al valor predeterminado ELSE NULLy obtienes el error. (Puede verificar las probabilidades si permite nulos en la columna y ejecuta el script SQLfiddle ).

De acuerdo con lo anterior, otra forma de resolverlo sería cambiar las expresiones para cumplir con la forma en que SQL-Server ejecuta el CASEy los 10 casos tienen la misma probabilidad:

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