2016-06-30 21 views
6

Tôi nhận được kết quả lẻ khi sử dụng NEWID() kết hợp với cột được tính liên tục. Tôi có sử dụng một số chức năng sai?Kết quả không nhất quán với cột được tính toán NEWID() và PERSISTED

Không sử dụng lâu dài khi tạo cột và do đó tính toán giá trị khi chọn cột, sẽ trả lại giá trị chính xác. Cập nhật cột (col1) cũng sẽ trả lại giá trị chính xác.

DECLARE @test TABLE (
    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)) 

SELECT * FROM @test 
UPDATE @test SET Col1 = Col1*1 
SELECT * FROM @test 

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

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

Tôi nghĩ đáng chú ý là người ta nhận được hành vi mong đợi bằng từ khóa 'PERSISTED' bị bỏ qua. Bạn có thể muốn gọi điều đó trong câu hỏi của bạn. –

+0

@DanGuzman Điểm tốt, đã cập nhật câu hỏi. – Kristofer

+2

được đăng chéo tới [dba.se] (http://dba.stackexchange.com/q/142675/68127) –

Trả lời

4

Dường như, công cụ truy vấn tính toán số ngẫu nhiên hai lần cho mỗi hàng.

Lần đầu tiên cho Col1, lần thứ hai cho tuyên bố CASE của cột được duy trì.

Trình tối ưu hóa không biết hoặc không quan tâm đến trường hợp này là NEWID là hàm không xác định và gọi hàm đó hai lần.

Thực ra, thậm chí có thể không có sự lựa chọn. Bạn có muốn người tối ưu hóa tạo bảng tạm thời đằng sau hậu trường hay không, điền vào số Col1 với kết quả biểu thức tạo số ngẫu nhiên, sau đó đọc bảng tạm thời đó và sử dụng các kết quả trung gian đã lưu này để tính kết quả của biểu thức CASE, sau đó thực hiện final INSERT? Trong trường hợp này, nó là rẻ hơn cho người tối ưu hóa để tính toán biểu thức hai lần mà không cần viết kết quả trung gian vào đĩa. Trong một số trường hợp khác (ví dụ, khi bạn không có 5, nhưng 5 tỷ hàng hoặc chỉ số bổ sung), chi phí ước tính có thể khác và hành vi này sẽ thay đổi.

Tôi không nghĩ bạn có thể làm được gì nhiều. Chỉ cần nhận thức được hành vi này. Luôn lưu một cách rõ ràng tập hợp các số ngẫu nhiên được tạo ra vào một bảng, sau đó thực hiện các phép tính tiếp theo dựa trên chúng.

Tôi đã sao chép nó trong SQL Server 2008 và 2014. Đây là một kế hoạch thực hiện mà tôi có trong SQL Server 2008, nhưng nó không thực sự thú vị. Trong năm 2014 kế hoạch là như nhau, ngoại trừ không có nhà điều hành Top.

plan 2008

Constant Scan điều hành ra một danh sách Union1009, được sử dụng trong Compute Scalar sau. Tôi đoán, nó đi xuống để thực hiện chi tiết của các nhà điều hành Constant Scan và/hoặc Compute Scalar.

Hành vi quan sát cho chúng tôi biết rằng newid() được gọi hai lần mỗi hàng tại đây.

+0

Kỳ lạ thay, kế hoạch thực hiện cho thấy các giá trị ngẫu nhiên chỉ được tính một lần. Chúng được tính như là một phần của quá trình quét liên tục. Sau đó, một đại lượng tính toán tính cột được tính toán. – usr

+0

@usr, tôi thấy toán tử 'Constant scan' và sau đó là' Compute Scalar' để tính toán 'CASE' sử dụng kết quả đầu ra từ' Quét liên tục'.Tôi không thấy rõ ràng trong kế hoạch rằng kết quả của toán tử 'Constant scan' được lưu trữ ở đâu đó trong bộ nhớ và không tính toán lại khi cần thiết. Trong mọi trường hợp, hành vi quan sát cho chúng ta biết rằng 'NEWID()' được gọi hai lần mỗi hàng. –

+0

Tôi đồng ý đó là vấn đề. Nó chỉ không hiển thị trong kế hoạch. Một người có kiến ​​thức sâu bên trong có lẽ có thể giải thích cách thực hiện điều này. Thông thường, vô hướng được tính lười biếng và một lần. Không có bảng tạm thời cần thiết cho điều đó. – usr

1

Trong khi thử nghiệm, tôi đã xóa các chức năng không liên quan đến NEWID và hiển thị kết quả nếu NEWID được tính trước thời hạn. Nó có thể hữu ích cho người khác.

DECLARE @test TABLE (
InsertType VARCHAR(30), 
Col1 VARCHAR(5), 
Contains2 AS CASE WHEN (Col1) LIKE '%2%' THEN 1 ELSE 0 END) --depends on Col1 

INSERT INTO @test (InsertType, Col1) VALUES 
    ('Compute With Insert', LEFT(NEWID(), 5)), 
    ('Compute With Insert', LEFT(NEWID(), 5)), 
    ('Compute With Insert', LEFT(NEWID(), 5)), 
    ('Compute With Insert', LEFT(NEWID(), 5)), 
    ('Compute With Insert', LEFT(NEWID(), 5)) 

SELECT * FROM @test 

DECLARE @A VARCHAR(5) = LEFT(NEWID(), 5); 
DECLARE @B VARCHAR(5) = LEFT(NEWID(), 5); 
DECLARE @C VARCHAR(5) = LEFT(NEWID(), 5); 
DECLARE @D VARCHAR(5) = LEFT(NEWID(), 5); 
DECLARE @E VARCHAR(5) = LEFT(NEWID(), 5); 

SELECT @A, @B, @C, @D, @E; 

INSERT INTO @Test (InsertType, Col1) VALUES 
('Compute Before Insert', @A), ('Compute Before Insert', @B), ('Compute Before Insert', @C), ('Compute Before Insert', @D), ('Compute Before Insert', @E) 

SELECT * FROM @test 

InsertType     Col1  Contains2 
Compute With Insert  C5507  0 
Compute With Insert  C17D7  0 
Compute With Insert  D9087  1 
Compute With Insert  E2DB0  0 
Compute With Insert  7D1AF  1 
Compute Before Insert  31050  0 
Compute Before Insert  2954C  1 
Compute Before Insert  9E205  1 
Compute Before Insert  DDF05  0 
Compute Before Insert  ED708  0 
Các vấn đề liên quan