9

Tôi đang cố gọi TVF hai lần với các tham số khác nhau trong cùng một truy vấn, nhưng vì một số lý do, khi tôi tham gia kết quả cùng nhau, một trong các kết quả loại mặt nạ khác. Tôi đã giảm vấn đề của tôi xuống ví dụ nhỏ này:Gọi TVF đa tuyên bố với các thông số khác nhau trong CTE riêng biệt cho kết quả sai

Đi này TVF inline:

CREATE FUNCTION dbo.fnTestErrorInline(@Test INT) 
RETURNS TABLE 
AS 
RETURN 
(
    SELECT ID, Val 
    FROM (VALUES 
     (1, 1, 10), 
     (1, 2, 20), 
     (1, 3, 30), 
     (1, 4, 40), 
     (2, 1, 50), 
     (2, 2, 60), 
     (2, 3, 70), 
     (2, 4, 80) 
    ) t(Test, ID, Val) 
    WHERE [email protected] 
) 

và một chức năng multiline tương đương:

CREATE FUNCTION dbo.fnTestErrorMultiline(@Test INT) 
RETURNS @tbl TABLE (
    ID INT NOT NULL, 
    Val INT NOT NULL 
) 
AS 
BEGIN 
    IF @Test=1 
    INSERT INTO @tbl (ID, Val) VALUES 
    (1, 10), 
    (2, 20), 
    (3, 30), 
    (4, 40); 

    IF @Test=2 
    INSERT INTO @tbl (ID, Val) VALUES 
    (1, 50), 
    (2, 60), 
    (3, 70), 
    (4, 80); 

    RETURN 
END 

Nếu tôi chạy truy vấn này:

WITH cte1 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorInline(1) 
    GROUP BY ID 
), cte2 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorInline(2) 
    GROUP BY ID 
) 
SELECT * 
FROM cte1 c1 
INNER JOIN cte2 c2 ON c1.ID=c2.ID; 

kết quả như mong đợi:

ID Total ID Total 
1 10 1 50 
2 20 2 60 
3 30 3 70 
4 40 4 80 

nhưng khi tôi sử dụng phiên bản multiline của hàm:

WITH cte1 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorMultiline(1) 
    GROUP BY ID 
), cte2 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorMultiline(2) 
    GROUP BY ID 
) 
SELECT * 
FROM cte1 c1 
INNER JOIN cte2 c2 ON c1.ID=c2.ID; 

kết quả là không chính xác - cte2 cho thấy các giá trị tương tự như cte1:

ID Total ID Total 
1 10 1 10 
2 20 2 20 
3 30 3 30 
4 40 4 40 

Ngoài ra, tôi chỉ nhìn thấy hành vi này khi có GROUP BY. Không có nó, kết quả là tốt.

Kỳ lạ thay, nếu tôi thêm một cột vào CTE thứ hai, nó làm thay đổi kết quả:

WITH cte1 AS (
    SELECT ID, SUM(Val) AS Total 
    FROM dbo.fnTestErrorMultiline(1) 
    GROUP BY ID 
), cte2 AS (
    SELECT ID, SUM(Val) AS Total, SUM(Val+0) AS why 
    FROM dbo.fnTestErrorMultiline(2) 
    GROUP BY ID 
) 
SELECT * 
FROM cte1 c1 
INNER JOIN cte2 c2 ON c1.ID=c2.ID; 

mang

ID Total ID Total why 
1 50 1 50 50 
2 60 2 60 60 
3 70 3 70 70 
4 80 4 80 80 

Nó xuất hiện cột phụ cần tham khảo một cột trong bảng TVF - một giá trị không đổi không thay đổi kết quả.

Điều gì đang xảy ra ở đây? Bạn không phải gọi một TVF nhiều dòng hơn một lần cho mỗi truy vấn?

Tôi đã thử nghiệm này trên SQL Server 2008 R2 và 2012

+0

Đang chuyển từ 3 đến 'fnTestErrorMultiline' trong CTE thứ hai, tôi vẫn có thể xem kết quả. http://sqlfiddle.com/#!6/e0395/13. –

+0

@ MM93 lạ kỳ lạ của nó! Điều đó dường như ngụ ý hàm chỉ chạy một lần vì CTE thứ hai sẽ không có bất kỳ hàng nào để tham gia. Nhìn vào kế hoạch truy vấn, nó hiển thị 'Bảng Giá trị Chức năng' hai lần, vì vậy tôi thực sự không biết điều gì đang xảy ra – David

+0

Nếu tôi thay đổi thứ tự (tức là) chuyển '3' sang' fnTestErrorMultiline' trong CTE đầu tiên, tôi không thể thấy kết quả –

Trả lời

7

Đây là một lỗi được biết đến trong SQL Server, nơi nó không đúng cách có thể ống chỉ các kết quả cho một ví dụ của TVF và phát lại chúng cho người khác (mặc dù thực tế là người kia có các tham số khác nhau và trả về các kết quả khác nhau).

Lỗi đã tồn tại một thời gian nhưng những thay đổi gần đây đối với ước tính số lượng thẻ có nghĩa là trong năm 2014+ thậm chí có nhiều khả năng xảy ra vấn đề này hơn.

Xem kết nối mục ..

NB: Kế hoạch thực hiện trông như sau.

enter image description here

Nó sử dụng một Common Subexpression Spool Cả ba nhấn mạnh cuộn trên thực tế cùng một đối tượng, trong điều hành vàng các hàng được chèn vào và sau đó chúng được tái hiện lại trong các nhà khai thác màu xanh lá cây.

Thêm

OPTION (QUERYRULEOFF GenGbApplySimple, QUERYRULEOFF BuildGbApply) 

tránh vấn đề này và đưa ra một kế hoạch khác nhau với kết quả đúng nhưng đây không phải là điều mà tôi sẽ sử dụng trong sản xuất.

enter image description here

+0

Bạn có tránh các tùy chọn truy vấn đó vì lý do hiệu suất hoặc một số lý do khác không? Tôi có thể kiểm tra truy vấn của mình với các tùy chọn này so với việc tạo truy vấn khác nhau (biến bảng để giữ kết quả hoặc thứ gì đó) để xem cách thực hiện, trừ khi các tùy chọn có một số tác dụng phụ tiêu cực khác. Truy vấn cụ thể này không chạy thường xuyên, vì vậy tôi có thể chịu đựng được hiệu suất truy cập nếu đó là tất cả. – David

+1

Và wow, tôi chỉ xem xét kỹ hơn vấn đề kết nối đầu tiên mà bạn đã liên kết. Ví dụ của chúng tôi gần như giống hệt nhau. – David

+1

@David chúng không có giấy tờ, không được hỗ trợ, vv vật chất hóa thành một biến bảng sẽ hoạt động. Hoặc đơn giản là sao chép định nghĩa hàm và có hai phiên bản giống hệt nhau với các tên khác nhau, do đó bạn chỉ cần tránh tự tham gia cũng sẽ hoạt động. –

Các vấn đề liên quan