Giá trị Null trong một tuyên bố CASE


8

Tôi đang chơi xung quanh với một số thứ trong SSMS để tìm hiểu thêm một chút khi tôi học cho kỳ thi 70-461 của mình và tôi đã bắt gặp một chút nôn nao. Tôi đang cố gắng tạo một bảng để chơi xung quanh vì vậy tôi không phải thay đổi / xóa bất kỳ bảng nào đã được tạo trong cơ sở dữ liệu AdventureWorks hoặc TSQL2012. Tôi đã tạo một bảng tạm thời để kiểm tra mã của mình trước khi tôi thực sự tạo một bảng để chơi và đây là mã tôi đang sử dụng để chèn các giá trị vào bảng của mình:

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;

Vấn đề tôi gặp phải là tôi liên tục gặp lỗi khi nói "Không thể chèn giá trị NULL vào cột 'Quốc gia', bảng 'tempdb.dbo. # TestEmployeeCountry" Lý do là vì tôi có cột Quốc gia được đặt thành KHÔNG NULL và mã của tôi không hoạt động đối với một số phần chèn, vấn đề là tôi ngẫu nhiên nhận được các giá trị NULL từ tuyên bố trường hợp của mình.

Tôi biết rằng để khắc phục điều này, tôi có thể dễ dàng thêm một dòng khác có nội dung "DEFAULT xxxxxx" tuy nhiên tôi muốn hiểu điều gì đang xảy ra vì dựa trên những gì tôi thấy tôi không nên làm điều đó, phải không? Tôi nghĩ rằng tôi đã viết chính xác trường hợp của mình, chỉ cho tôi một số trong khoảng từ 1-10 và khi kiểm tra chỉ một câu lệnh chọn cụ thể trên 1000 lần thử, tôi luôn nhận được một số ngẫu nhiên trong khoảng từ 1-10, không có gì lớn hơn hoặc nhỏ hơn. Bất cứ ai có thể giúp tôi hiểu tại sao mã này cố gắng nhập giá trị NULL vào cột đó?

Câu trả lời:


8

Tại sao điều này xảy ra đã được trả lời bởi @PaulWhite trong câu hỏi SO: Biểu thức CASE này đạt đến mệnh đề ELSE như thế nào?

Để giải quyết nó, bạn nên tính toán ABS(CHECKSUM(NEWID()))%10 +1bên ngoài / trước INSERTcâu lệnh để nó được tính một lần. Cái gì đó như:

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 ;

Cũng lưu ý rằng với mã của bạn, 10 quốc gia sẽ không được đặt trong bảng với xác suất bằng nhau! Quốc gia đầu tiên ( USA) sẽ có 10% cơ hội, quốc gia thứ hai sẽ có 9% ( (100%-10%)*10%), 8.1% thứ ba, ( (100%-19%)*10%), v.v ... Điều đó để lại một cơ hội không nhỏ (xung quanh 1/e) mà không ai trong số 10 được chọn và CASEbiểu thức đi đến mặc định ELSE NULLvà bạn nhận được lỗi. (Bạn có thể kiểm tra xác suất nếu bạn cho phép null trong cột và chạy tập lệnh SQLfiddle .)

Theo như trên, một cách khác để giải quyết nó là thay đổi các biểu thức để tuân thủ cách SQL-Server thực thi CASEvà tất cả 10 trường hợp có cùng xác suất:

    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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.