SQL Server có điền các cột PERSISTED với dữ liệu không khớp với định nghĩa không?


16

Tôi đang theo dõi câu hỏi này về các giá trị lạ trong một PERSISTEDcột được tính toán. Câu trả lời ở đó đưa ra một vài phỏng đoán về cách hành vi này xảy ra.

Tôi đang hỏi như sau: Đây có phải là một lỗi hoàn toàn không? Các PERSISTEDcột có bao giờ được phép hành xử theo cách này?

DECLARE @test TABLE (
    Col1 INT,
    Contains2 AS CASE WHEN 2 IN (Col1) THEN 1 ELSE 0 END PERSISTED) --depends on Col1

INSERT INTO @test (Col1) VALUES
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5))

SELECT * FROM @test --shows impossible data

UPDATE @test SET Col1 = Col1*1 --"fix" the data by rewriting it

SELECT * FROM @test --observe fixed data

/*
Col1    Contains2
2   0
2   0
0   1
4   0
3   0

Col1    Contains2
2   1
2   1
0   0
4   0
3   0
*/

Lưu ý rằng dữ liệu xuất hiện "không thể" vì các giá trị của cột được tính toán không tương ứng với định nghĩa của nó.

Người ta biết rằng các hàm không xác định trong các truy vấn có thể hoạt động kỳ lạ nhưng ở đây điều này dường như vi phạm hợp đồng của các cột được tính toán bền vững và do đó, là bất hợp pháp.

Chèn số ngẫu nhiên có thể là một kịch bản có sẵn nhưng nếu chúng ta chèn NEWID()giá trị hay SYSUTCDATETIME()thì sao? Tôi nghĩ rằng đây là một vấn đề có liên quan mà thực tế có thể tự biểu hiện.

Câu trả lời:


9

Đây chắc chắn là một lỗi. Thực tế là các col1giá trị xảy ra là kết quả của một biểu thức liên quan đến các số ngẫu nhiên rõ ràng không thay đổi giá trị chính xác được cho col2là gì. DBCC CHECKDBtrả về một lỗi nếu điều này được chạy với một bảng cố định.

create table test (
    Col1 INT,
    Contains2 AS CASE WHEN 2 IN (Col1) THEN 1 ELSE 0 END PERSISTED);

INSERT INTO test (Col1) VALUES
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5)),
    (ABS(CHECKSUM(NEWID()) % 5));

DBCC CHECKDB

Cung cấp (cho lần chạy thử nghiệm của tôi có một hàng "không thể")

Msg 2537, Level 16, State 106, Line 17
Table error: object ID 437576597, index ID 0, partition ID 72057594041008128, alloc unit ID 72057594046251008 (type In-row data), page (1:121), row 0. The record check (valid computed column) failed. The values are 2 and 0.
DBCC results for 'test'.
There are 5 rows in 1 pages for object "test".
CHECKDB found 0 allocation errors and 1 consistency errors in table 'test' (object ID 437576597).

Nó cũng báo cáo rằng

Repair_allow_data_loss là mức sửa chữa tối thiểu cho các lỗi được tìm thấy bởi DBCC CHECKDB

Và nếu được đưa lên tùy chọn sửa chữa sẽ xóa toàn bộ hàng vì nó không có cách nào để biết cột nào bị hỏng.

Đính kèm trình gỡ lỗi cho thấy rằng NEWID()đang được đánh giá hai lần cho mỗi hàng được chèn. Một lần trước khi CASEbiểu thức được ước tính và một lần bên trong nó.

nhập mô tả hình ảnh ở đây

Một cách giải quyết khác có thể là sử dụng

INSERT INTO @test
            (Col1)
SELECT ( ABS(CHECKSUM(NEWID()) % 5) )
FROM   (VALUES (1),(1),(1),(1),(1)) V(X); 

Mà vì lý do này hay lý do khác tránh vấn đề và chỉ đánh giá biểu thức một lần mỗi hàng.


2

Theo cuộc trò chuyện bình luận, sự đồng thuận dường như là câu trả lời cho câu hỏi của OP là điều này không phải là một lỗi (nghĩa là bất hợp pháp).

OP tham khảo phân tích của Vladimir Baranov trên StackOverflow, nơi họ tuyên bố:

"Lần đầu tiên cho Col1, lần thứ hai cho câu lệnh CASE của cột được duy trì.

Optimiser không biết, hoặc không quan tâm trong trường hợp này rằng NEWID là một hàm không xác định và gọi nó là hai lần. "

Nói cách khác, nên dự kiến ​​rằng [NEWID () bên trong] col1 có cùng giá trị bạn vừa chèn như khi bạn thực hiện phép tính.

Điều này đồng nghĩa với những gì xảy ra với lỗi, trong đó NEWID được tạo cho Col1 và sau đó được tạo lại cho cột được duy trì:

INSERT INTO @Test (Col1, Contains2) VALUES
(NEWID(), CASE WHEN (NEWID()) LIKE '%2%' THEN 1 ELSE 0 END)

Trong thử nghiệm của tôi, các hàm không xác định khác như RAND và giá trị thời gian không dẫn đến cùng một lỗi.

Theo Martin, điều này đã được nâng lên thành Microsoft ( https://connect.microsoft.com/QueryServer/Feedback/Details/2751288 ) trong đó có các nhận xét quay lại trang này và phân tích StackOverflow (bên dưới).

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.