Quét liên tục quét


14

Tôi có một cái bàn với vài chục hàng. Thiết lập đơn giản là sau

CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

Và tôi có một truy vấn nối bảng này với một tập hợp các hàng được xây dựng giá trị bảng (được tạo bởi các biến và hằng), như

DECLARE @id1 int = 101, @id2 int = 105;

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (@id1, 'A'),
        (@id2, 'B')
    ) p([Id], [Code])
    FULL JOIN #data d ON d.[Id] = p.[Id];

Kế hoạch thực hiện truy vấn đang cho thấy quyết định của trình tối ưu hóa là sử dụng FULL LOOP JOINchiến lược, điều này có vẻ phù hợp, vì cả hai đầu vào đều có rất ít hàng. Tuy nhiên, một điều tôi nhận thấy (và không thể đồng ý) là các hàng TVC đang được lưu trữ (xem khu vực của kế hoạch thực hiện trong hộp màu đỏ).

Quét liên tục quét

Tại sao trình tối ưu hóa giới thiệu spool ở đây, lý do để làm điều đó là gì? Không có gì phức tạp ngoài ống chỉ. Hình như không cần thiết. Làm thế nào để thoát khỏi nó trong trường hợp này, những cách có thể là gì?


Kế hoạch trên đã đạt được trên

Máy chủ Microsoft SQL 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)


Gợi ý liên quan tại feedback.azure.com
i-one

Câu trả lời:


19

Tại sao trình tối ưu hóa giới thiệu spool ở đây, lý do để làm điều đó là gì? Không có gì phức tạp ngoài ống chỉ.

Thứ nằm ngoài bộ đệm không phải là một tham chiếu bảng đơn giản, mà đơn giản có thể được nhân đôi khi tạo ra sự thay thế tham gia / chống bán tham gia trái .

Nó có thể trông hơi giống một bảng (Quét liên tục) nhưng với trình tối ưu hóa * nó là một UNION ALLtrong các hàng riêng biệt trong VALUESmệnh đề.

Độ phức tạp bổ sung là đủ để trình tối ưu hóa chọn bộ đệm và phát lại các hàng nguồn và không thay thế bộ đệm bằng "bảng lấy" đơn giản sau này. Ví dụ: chuyển đổi ban đầu từ tham gia đầy đủ trông như thế này:

kế hoạch sớm

Lưu ý các cuộn chỉ thêm được giới thiệu bởi biến đổi chung. Các cuộn trên một bảng đơn giản được làm sạch sau theo quy tắc SpoolGetToGet.

Nếu trình tối ưu hóa có một SpoolConstGetToConstGetquy tắc tương ứng , về nguyên tắc, nó có thể hoạt động như bạn muốn.

Làm thế nào để thoát khỏi nó trong trường hợp này, những cách có thể là gì?

Sử dụng bảng thực (tạm thời hoặc biến) hoặc viết chuyển đổi từ tham gia đầy đủ theo cách thủ công, ví dụ:

WITH 
    p([Id], [Code]) AS
    (
        SELECT @id1, 'A'
        UNION ALL
        SELECT @id2, 'B'
    ),
    FullJoin AS
    (
        SELECT
            p.Code,
            d.[Status]
        FROM p
        LEFT JOIN #data d 
            ON d.[Id] = p.[Id]
        UNION ALL
        SELECT
            NULL,
            D.[Status]
        FROM #data AS D
        WHERE NOT EXISTS
        (
            SELECT *
            FROM p
            WHERE p.Id = D.Id
        )
    )
SELECT
    COALESCE(FullJoin.Code, 'X') AS Code,
    COALESCE(FullJoin.Status, 0) AS [Status]
FROM FullJoin;

Lập kế hoạch viết lại thủ công:

Kế hoạch viết lại bằng tay

Điều này có chi phí ước tính là 0,0067201, so với 0,0203412 đơn vị cho bản gốc.


* Nó có thể được quan sát như một LogOp_UnionAlltrong những cây được chuyển đổi (TF 8605). Trong Cây đầu vào (TF 8606), nó là a LogOp_ConstTableGet. Các cây được chuyển đổi chương trình cây của các yếu tố biểu hiện tối ưu hóa sau khi phân tích, bình thường, algebrization, ràng buộc, và một số công việc chuẩn bị khác. Các Input Tree cho thấy các yếu tố sau đó chuyển sang Phủ Normal Form (NNF chuyển đổi), liên tục thời gian chạy sụp đổ, và một vài bit và Bobs khác. Chuyển đổi NNF bao gồm logic để thu gọn các hiệp hội logic và bảng chung được, trong số những thứ khác.


3

Bộ đệm bảng chỉ đơn giản là tạo một bảng trong số hai bộ dữ liệu có trong VALUESmệnh đề.

Bạn có thể loại bỏ bộ đệm bằng cách chèn các giá trị đó vào bảng tạm thời, như sau:

DROP TABLE IF EXISTS #data;
CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

DROP TABLE IF EXISTS #p;
CREATE TABLE #p
(
    Id int NOT NULL
    , Code char(1) NOT NULL
);

DECLARE @id1 int = 101, @id2 int = 105;

INSERT INTO #p (Id, Code)
VALUES
        (@id1, 'A'),
        (@id2, 'B');


SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM #p p
    FULL JOIN #data d ON d.[Id] = p.[Id];

Nhìn vào kế hoạch thực hiện cho truy vấn của bạn, chúng tôi thấy danh sách đầu ra chứa hai cột sử dụng Uniontiền tố; đây là một gợi ý rằng bộ đệm đang tạo một bảng từ nguồn union'd:

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

Các FULL OUTER JOIN đòi hỏi SQL Server để truy cập vào các giá trị trong phai lần, một lần cho mỗi "mặt" của kết nối. Tạo một ống chỉ cho phép các vòng lặp bên trong kết hợp tham gia để truy cập dữ liệu được lưu.

Thật thú vị, nếu bạn thay thế FULL OUTER JOINbằng a LEFT JOINvà a RIGHT JOINUNIONkết quả cùng nhau, SQL Server không sử dụng bộ đệm.

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    LEFT JOIN #data d ON d.[Id] = p.[Id]
UNION
SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    RIGHT JOIN #data d ON d.[Id] = p.[Id];

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

Lưu ý, tôi không đề xuất sử dụng UNIONtruy vấn ở trên; đối với các bộ đầu vào lớn hơn, nó có thể không hiệu quả hơn các đơn giản FULL OUTER JOINbạn đã có.


Trong khối lượng công việc thực sự của bạn, spool có thực sự tốn kém?
Max Vernon
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.