ดัชนีที่กรองจะใช้ก็ต่อเมื่อส่วนที่กรองอยู่ใน JOIN ไม่ใช่ WHERE


10

ฉันได้สร้างดัชนีที่กรองแล้วด้านล่าง แต่เมื่อฉันเรียกใช้ 2 คิวรีเพิ่มเติมลงดัชนีนี้จะถูกใช้สำหรับการค้นหาในตัวอย่างแรกที่มี END_DTTM ใน JOIN มากกว่าข้อที่ (นั่นคือข้อแตกต่างในแบบสอบถามเท่านั้น) . ใครช่วยอธิบายได้ว่าทำไมสิ่งนี้ถึงเกิดขึ้น

การสร้างดัชนี

CREATE NONCLUSTERED INDEX [ix_PATIENT_LIST_BESPOKE_LIST_ID_includes] ON [dbo].[PATIENT_LIST_BESPOKE] 
(
    [LIST_ID] ASC,
    [END_DTTM] ASC
)
WHERE ([END_DTTM] IS NULL)

แบบสอบถาม

DECLARE @LIST_ID INT = 3655

--This one seeks on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
                                      AND PATIENT_LIST_BESPOKE.END_DTTM IS NULL
WHERE
    PATIENT_LISTS.LIST_ID = @LIST_ID

--This one scans on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
WHERE   
    PATIENT_LISTS.LIST_ID = @LIST_ID AND
    PATIENT_LIST_BESPOKE.END_DTTM IS NULL   

คำตอบ:


12

เพื่อให้เครื่องมือเพิ่มประสิทธิภาพตรงกับเพรดิเคตกับดัชนี (กรองหรืออื่น ๆ ) เพรดิเคตต้องปรากฏขึ้นติดกับการดำเนินการรับในแผนผังคิวรีแบบตรรกะ เพื่ออำนวยความสะดวกในเรื่องนี้ภาคส่วนโดยทั่วไปแล้วจะผลักดันให้ใกล้ที่สุดเท่าที่จะทำได้เพื่อออกจากต้นไม้ตรรกะก่อนที่จะเริ่มการปรับให้เหมาะสม

เพื่อให้ง่ายขึ้นอย่างมากการใช้กลยุทธ์ดัชนีทางกายภาพจึงทำสิ่งนี้

Predicate + Logical Get -> Physical Get (using Index)

แบบสอบถามที่คุณสนใจเริ่มต้นด้วยภาคแสดงด้านบนเข้าร่วมด้านนอก:

Predicate on T2 --+-- LOJ -- Get (T1)
                       |
                       +---- Get (T2)

รูปร่างนี้ไม่ตรงกับกฎกลยุทธ์ดัชนีเนื่องจากเพรดิเคตไม่ติดกับ Get ดังนั้นส่วนแรกของคำตอบคือการจับคู่ดัชนีที่กรองแล้วจะล้มเหลวเว้นแต่ว่าคำกริยาสามารถผลักผ่านการรวมภายนอก

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

ดังนั้นการจับคู่ดัชนีที่กรองจึงล้มเหลวในกรณีนี้ เพื่อความชัดเจนการเขียนใหม่จะถูกต้องในกรณีที่เฉพาะเจาะจงมากที่คุณอ้างถึง (แบบสอบถามที่สอง)

สำหรับฟอร์มแบบสอบถามแรก (ที่มีซีแมนทิกส์แตกต่างกัน) ภาคแสดงจะเชื่อมโยงกับการเข้าร่วมตั้งแต่เริ่มต้นและตรรกะแบบเลื่อนลงของภาคแสดงสามารถย้ายสิ่งนี้ได้ในระยะทางสั้น ๆ เพื่อรับเนื่องจากมันไม่จำเป็นต้องเลื่อนผ่าน อธิบายไว้ข้างต้น

ความเป็นมาและข้อมูลเพิ่มเติม:


9

แบบสอบถามเหล่านี้ไม่ได้มีความหมายเดียวกันเนื่องจากหนึ่งสามารถกรองก่อนที่จะเข้าร่วมอื่น ๆ สามารถกรองหลังจาก ให้ฉันอธิบายด้วยตัวอย่างที่ง่ายกว่า:

CREATE TABLE dbo.Lefty(LeftyID INT PRIMARY KEY);

CREATE TABLE dbo.Righty(LeftyID INT, SomeList INT);

INSERT dbo.Lefty(LeftyID) VALUES(1),(2),(3);

INSERT dbo.Righty(LeftyID, SomeList) VALUES(1,1),(1,NULL),(2,2);

Query 1 ส่งคืนทั้งสามแถว:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
AND r.SomeList IS NULL;

อย่างไรก็ตามการค้นหา 2 ออกจาก LeftyID 2:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
WHERE r.SomeList IS NULL;

หลักฐาน SQLfiddle

ถ้าคุณกำลังพยายามดำเนินการป้องกันกึ่งเข้าร่วมคอลัมน์ทดสอบความต้องการที่จะไม่ nullable เกณฑ์การย้ายระหว่าง ON และ WHERE ไม่สร้างความแตกต่างทางตรรกะเมื่อคุณติดต่อกับ INNER เข้าร่วมเท่านั้น แต่ด้วย OUTER จะมีความแตกต่างที่สำคัญ และคุณควรให้ความสำคัญกับผลลัพธ์ของคุณว่าถูกต้องมากกว่าหรือไม่ว่าจะสามารถใช้ดัชนีที่กรองได้หรือไม่


ขอบคุณสำหรับคำตอบ แต่ฉันไม่ได้อ้างข้อความค้นหาเหมือนกันฉันถามว่าเพราะเหตุใดแบบสอบถามหนึ่งจึงใช้ดัชนีที่กรองแล้ว แต่คำถามอื่นไม่ได้
chris

@chris คุณพยายามบังคับดัชนีนั้นด้วยคำใบ้ดัชนีหรือไม่? ฉันอยากรู้ว่าจะเปรียบเทียบแผนจริงหลังการดำเนินการกับและไม่มีคำใบ้นั้นหรือไม่ สำหรับฉันมันชัดเจนว่าเครื่องมือเพิ่มประสิทธิภาพนั้นไม่สนใจดัชนีนั้นเมื่อเชื่อว่ามันกำลังทำการรวมการต่อต้านกึ่ง (เนื่องจากมันไม่คาดว่าจะใช้คอลัมน์ nullable ในกรณีนั้น) แต่ฉันไม่แน่ใจว่าจะใช้หรือไม่ ทำอย่างไรกับการคิดต้นทุนหรือคำสั่งของการดำเนินการหรือความรู้พื้นฐานบางอย่างที่อาจมีแถวจำนวนมากที่มาจากด้านซ้ายมากกว่าแถวที่อยู่ในดัชนีที่กรอง การเห็นแผนงานอาจช่วยได้
Aaron Bertrand

3

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

-- 1st query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    LEFT JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID ;           -- and the join

และที่สอง:

-- 2nd query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID             -- and the join

UNION ALL

SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
WHERE NOT EXISTS  
      ( SELECT *
        FROM   DBO.PATIENT_LIST_BESPOKE AS b
        WHERE  a.LIST_ID = b.LIST_ID         -- but not for this
      ) ;

ฉันคิดว่าตอนนี้ค่อนข้างชัดเจนว่าสำหรับส่วนที่ 2 ของแบบสอบถาม 2nq ดัชนีที่กรองแล้วไม่สามารถใช้งานได้


โดยละเอียดเกี่ยวกับข้อความค้นหาเหล่านี้มีLIST_IDค่า4 ประเภทในตารางแรก:

  • (ก) END_DTTM IS NULLค่าที่ได้ตรงกับแถวในตารางที่สองทั้งหมดที่มี

  • (ข) ค่าที่มีการจับคู่แถวในตารางที่สองทั้งที่มีและมีการEND_DTTM IS NULLEND_DTTM IS NOT NULL

  • (ค) END_DTTM IS NOT NULLค่าที่มีการจับคู่แถวในตารางที่สองทั้งหมดที่มี

  • (d) ค่าที่ไม่มีแถวที่ตรงกันในตารางที่สอง

ตอนนี้เคียวรีที่ 1 จะส่งคืนค่าทั้งหมดของประเภท (a) และ (b) อาจเป็นไปได้หลายครั้ง (มากที่สุดเท่าที่พวกเขามีแถวที่ตรงกันในตารางที่สองด้วยEND_DTTM IS NULL) และแถวประเภท (c) และ (d) ทุกครั้ง นั่นคือส่วนที่ไม่เข้าคู่กันของการรวมภายนอก)

แบบสอบถามที่ 2 จะส่งกลับค่าทั้งหมดของประเภท (a) และ (b) อาจเป็นไปได้หลายครั้ง (มากที่สุดเท่าที่พวกเขามีแถวที่ตรงกันในตารางที่สองด้วยEND_DTTM IS NULL) และทุกแถวประเภท (d) เพียงครั้งเดียว
มันจะไม่ส่งคืนค่าประเภท (c) ใด ๆ เนื่องจากการเข้าร่วมจะค้นหาแถวที่ตรงกันในตารางที่สอง (แต่จะมีEND_DTTM IS NOT NULL) และพวกเขาจะถูกลบออกโดยWHEREประโยคถัดไป

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