สแกนแกนอย่างต่อเนื่อง


14

ฉันมีตารางที่มีแถวไม่กี่สิบแถว การตั้งค่าแบบง่ายกำลังติดตาม

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

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

และฉันมีแบบสอบถามที่รวมตารางนี้เข้ากับชุดของค่าตารางที่สร้างแถว (ทำจากตัวแปรและค่าคงที่) เช่น

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];

แผนการดำเนินการค้นหาแสดงให้เห็นว่าการตัดสินใจของเครื่องมือเพิ่มประสิทธิภาพคือการใช้FULL LOOP JOINกลยุทธ์ซึ่งดูเหมือนเหมาะสมเนื่องจากอินพุตทั้งสองมีแถวน้อยมาก แต่สิ่งหนึ่งที่ฉันสังเกตเห็น (และไม่สามารถตกลงกันได้) คือแถว TVC กำลังถูกสพูล (ดูพื้นที่ของแผนการดำเนินการในช่องสีแดง)

สแกนแกนอย่างต่อเนื่อง

ทำไมเครื่องมือเพิ่มประสิทธิภาพแนะนำสปูลที่นี่อะไรคือเหตุผลที่ต้องทำ ไม่มีอะไรซับซ้อนเกินกว่าสปูล ดูเหมือนว่ามันไม่จำเป็น วิธีกำจัดในกรณีนี้มีวิธีใดบ้างที่เป็นไปได้?


ได้รับแผนดังกล่าวข้างต้น

Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)


ข้อเสนอแนะที่เกี่ยวข้องที่ feedback.azure.com
i-one

คำตอบ:


19

ทำไมเครื่องมือเพิ่มประสิทธิภาพแนะนำสปูลที่นี่อะไรคือเหตุผลที่ต้องทำ ไม่มีอะไรซับซ้อนเกินกว่าสปูล

สิ่งที่เกินกว่าหลอดไม่ใช่การอ้างอิงตารางง่ายซึ่งก็สามารถทำซ้ำเมื่อซ้ายเข้าร่วม / กึ่งต่อต้านเข้าร่วมทางเลือกที่ถูกสร้างขึ้น

มันอาจจะดูคล้ายโต๊ะ (Constant Scan) เล็กน้อย แต่สำหรับ optimizer * มันเป็นหนึ่งUNION ALLในแถวที่แยกจากกันในVALUESอนุประโยค

ความซับซ้อนเพิ่มเติมนั้นเพียงพอสำหรับเครื่องมือเพิ่มประสิทธิภาพเพื่อเลือกสปูลและเล่นซ้ำแถวต้นฉบับและไม่แทนที่สปูลด้วย "ตารางรับ" แบบง่าย ๆ ในภายหลัง ตัวอย่างเช่นการแปลงเริ่มต้นจากการเข้าร่วมเต็มรูปแบบมีลักษณะดังนี้:

แผนต้น

สังเกตุสปูลเพิ่มเติมที่นำมาใช้โดยการแปลงทั่วไป SpoolGetToGetสิ่งของดังกล่าวข้างต้นได้รับตารางที่เรียบง่ายมีการทำความสะอาดขึ้นในภายหลังโดยกฎ

หากเครื่องมือเพิ่มประสิทธิภาพมีSpoolConstGetToConstGetกฎที่สอดคล้องกันก็สามารถใช้งานได้ตามหลักการ

วิธีกำจัดในกรณีนี้มีวิธีใดบ้างที่เป็นไปได้?

ใช้ตารางจริง (ชั่วคราวหรือตัวแปร) หรือเขียนการแปลงจากการเข้าร่วมเต็มรูปแบบด้วยตนเองตัวอย่างเช่น:

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;

แผนสำหรับการเขียนซ้ำด้วยตนเอง:

แผนเขียนซ้ำด้วยตนเอง

ค่าใช้จ่ายนี้มีค่าประมาณ 0.0067201 หน่วยเมื่อเทียบกับ 0.0203412 หน่วยสำหรับต้นฉบับ


* สามารถสังเกตได้ว่าเป็นLogOp_UnionAllในต้นไม้ที่ถูกแปลง (TF 8605) ในการป้อนข้อมูลต้นไม้ (TF 8606) LogOp_ConstTableGetมันเป็น แปลงต้นไม้แสดงให้เห็นว่าต้นไม้ขององค์ประกอบการแสดงออกเพิ่มประสิทธิภาพหลังจากแยกฟื้นฟู algebrization ผูกพันและบางส่วนเตรียมงานอื่น ๆ อินพุตต้นไม้แสดงให้เห็นถึงองค์ประกอบหลังจากการแปลงที่จะปฏิเสธปกติฟอร์ม (NNF แปลง) คงรันไทม์ยุบและไม่กี่บิตอื่น ๆ และ bobs การแปลง NNF รวมถึงตรรกะเพื่อยุบสหภาพตรรกะและตารางทั่วไปที่ได้รับเหนือสิ่งอื่นใด


3

สปูลตารางกำลังสร้างตารางจาก tuples สองชุดที่อยู่ในส่วนVALUESคำสั่ง

คุณสามารถกำจัดสปูลโดยการแทรกค่าเหล่านั้นลงในตาราง temp ก่อนเช่น:

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];

ดูที่แผนการดำเนินการสำหรับคิวรีของคุณเราจะเห็นรายการผลลัพธ์มีสองคอลัมน์ที่ใช้Unionคำนำหน้า นี่คือคำใบ้ว่าสปูลกำลังสร้างตารางจากแหล่งที่มารวม:

ป้อนคำอธิบายรูปภาพที่นี่

FULL OUTER JOINต้องใช้ SQL Server เพื่อเข้าถึงค่าในpสองครั้งครั้งสำหรับแต่ละ "ด้าน" ของการเข้าร่วม การสร้างสปูลอนุญาตให้วงด้านในที่เป็นผลลัพธ์เข้าร่วมในการเข้าถึงข้อมูลสปูล

ที่น่าสนใจถ้าคุณแทนที่FULL OUTER JOINด้วย a LEFT JOINและ a RIGHT JOINและUNIONผลลัพธ์ร่วมกัน SQL Server จะไม่ใช้สปูล

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];

ป้อนคำอธิบายรูปภาพที่นี่

หมายเหตุฉันไม่แนะนำให้ใช้UNIONแบบสอบถามด้านบน สำหรับชุดอินพุตขนาดใหญ่อาจไม่ได้มีประสิทธิภาพมากไปกว่าแบบง่ายที่FULL OUTER JOINคุณมีอยู่แล้ว


ในปริมาณงานจริงของคุณสปูลนั้นมีค่าใช้จ่ายสูงใช่หรือไม่
Max Vernon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.