แผนแบบสอบถามแปลกเมื่อใช้หรือในข้อเข้าร่วม - สแกนอย่างต่อเนื่องสำหรับทุกแถวในตาราง


10

ฉันกำลังพยายามสร้างแผนการสืบค้นตัวอย่างเพื่อแสดงว่าทำไมชุดผลลัพธ์สองชุดของยูเนี่ยนจึงดีกว่าการใช้ OR ในประโยค JOIN แผนแบบสอบถามที่ฉันเขียนทำให้ฉันนิ่งงัน ฉันใช้ฐานข้อมูล StackOverflow กับดัชนีที่ไม่ได้เป็นคลัสเตอร์ใน Users.Reputation

รูปภาพแผนแบบสอบถาม แบบสอบถามคือ

CREATE NONCLUSTERED INDEX IX_NC_REPUTATION ON dbo.USERS(Reputation)
SELECT DISTINCT Users.Id
FROM dbo.Users
INNER JOIN dbo.Posts  
    ON Users.Id = Posts.OwnerUserId
    OR Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5

แผนการสืบค้นอยู่ที่https://www.brentozar.com/pastetheplan/?id=BkpZU1MZEระยะเวลาการสืบค้นสำหรับฉันคือ 4:37 นาทีส่งคืนแถว 26612

ฉันไม่เคยเห็นรูปแบบการสแกนคงที่นี้ถูกสร้างขึ้นจากตารางที่มีอยู่ก่อนหน้านี้ - ฉันไม่คุ้นเคยกับสาเหตุที่มีการสแกนค่าคงที่สำหรับทุก ๆ แถวเมื่อการสแกนแบบคงที่มักใช้กับแถวเดียวที่ป้อนโดยผู้ใช้ เช่น SELECT GETDATE () ทำไมถึงใช้ที่นี่? ฉันขอขอบคุณคำแนะนำในการอ่านแผนแบบสอบถามนี้

ถ้าฉันแยก OR ออกเป็น UNION มันจะสร้างแผนมาตรฐานที่ใช้ใน 12 วินาทีโดยมี 26612 แถวเดียวกันกลับมา

SELECT Users.Id
FROM dbo.Users
    INNER JOIN dbo.Posts
       ON Users.Id = Posts.OwnerUserId
WHERE Users.Reputation = 5
UNION 
SELECT Users.Id
FROM dbo.Users
    INNER JOIN dbo.Posts
       ON  Users.Id = Posts.LastEditorUserId
WHERE Users.Reputation = 5

ฉันตีความแผนนี้เมื่อทำสิ่งนี้:

  • รับแถวทั้งหมด 41782500 จากโพสต์ (จำนวนแถวจริงตรงกับการสแกน CI บนโพสต์)
  • สำหรับแต่ละ 41782500 แถวในกระทู้:
    • ผลิตสเกลาร์:
    • Expr1005: OwnerUserId
    • Expr1006: OwnerUserId
    • Expr1004: ค่าคงที่ 62
    • Expr1008: LastEditorUserId
    • Expr1009: LastEditorUserId
    • Expr1007: ค่าคงที่ 62
  • ในการเรียงต่อกัน:
    • Exp1010: ถ้า Expr1005 (OwnerUserId) ไม่เป็นโมฆะให้ใช้อย่างอื่นใช้ Expr1008 (LastEditorUserID)
    • Expr1011: ถ้า Expr1006 (OwnerUserId) ไม่เป็นโมฆะให้ใช้งานนั้นมิฉะนั้นให้ใช้ Expr1009 (LastEditorUserId)
    • Expr1012: ถ้า Expr1004 (62) เป็นโมฆะให้ใช้มิเช่นนั้นให้ใช้ Expr1007 (62)
  • ในสเกลาร์คำนวณ: ฉันไม่รู้ว่าแอมเปอร์แซนด์ทำอะไร
    • Expr1013: 4 [และ?] 62 (Expr1012) = 4 และ OwnerUserId IS NULL (NULL = Expr1010)
    • Expr1014: 4 [และ?] 62 (Expr1012)
    • Expr1015: 16 และ 62 (Expr1012)
  • เรียงตาม:
    • Expr1013 ลำดับ
    • Expr1014 Asc
    • Expr1010 Asc
    • Expr1015 ลำดับ
  • ใน Merge Interval จะลบ Expr1013 และ Expr1015 (นี่คืออินพุต แต่ไม่ใช่เอาต์พุต)
  • ในการค้นหาดัชนีด้านล่างการวนซ้ำซ้อนกันการใช้ Expr1010 และ Expr1011 เป็นการค้นหาเพรดิเคต แต่ฉันไม่เข้าใจว่าจะเข้าถึงสิ่งเหล่านี้ได้อย่างไรเมื่อยังไม่ได้เข้าร่วมลูปซ้อนจาก IX_NC_REPUTATION กับทรีย่อยที่มี Expr1010 และ Expr1011 .
  • การเข้าร่วมลูปซ้อนจะส่งคืนเฉพาะ Users.ID ที่มีการจับคู่ในทรีย่อยก่อนหน้านี้ เนื่องจากการเลื่อนลงภาคแสดงผลแถวทั้งหมดที่ส่งคืนจากดัชนีค้นหาบน IX_NC_REPUTATION จะถูกส่งกลับ
  • การวนซ้ำซ้อนกันครั้งสุดท้ายเข้าร่วม: สำหรับแต่ละเรคคอร์ดการโพสต์เอาท์พุท Users.Id ที่พบการแข่งขันในชุดข้อมูลด้านล่าง

คุณลองกับคำถามย่อยหรือแบบสอบถามย่อย EXISTS หรือไม่ SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND ( EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.OwnerUserId) OR EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id = Posts.LastEditorUserId) ) ;
ypercubeᵀᴹ

แบบสอบถามย่อยหนึ่งรายการ:SELECT Users.Id FROM dbo.Users WHERE Users.Reputation = 5 AND EXISTS (SELECT 1 FROM dbo.Posts WHERE Users.Id IN (Posts.OwnerUserId, Posts.LastEditorUserId) ) ;
ypercubeᵀᴹ

คำตอบ:


10

แผนคือคล้ายกับที่ผมไปลงในรายละเอียดเพิ่มเติมได้ที่นี่

Postsตารางจะถูกสแกน

สำหรับแต่ละแถวมันสารสกัดและOwnerUserId LastEditorUserIdนี่คือในลักษณะที่คล้ายกับวิธีการUNPIVOTทำงาน คุณจะเห็นผู้ดำเนินการสแกนค่าคงที่เดียวในแผนสำหรับด้านล่างนี้สร้างแถวเอาต์พุตสองแถวสำหรับแต่ละแถวอินพุต

SELECT *
FROM dbo.Posts
UNPIVOT (X FOR U IN (OwnerUserId,LastEditorUserId)) Unpvt

ในกรณีนี้แผนจะซับซ้อนกว่านี้เล็กน้อยเนื่องจากซีแมนทิกส์ใช้สำหรับorว่าหากค่าคอลัมน์ทั้งคู่เหมือนกันเพียงหนึ่งแถวเท่านั้นที่ควรปล่อยจากการเข้าร่วมUsers(ไม่ใช่สอง)

สิ่งเหล่านี้จะถูกนำมาใส่ในช่วงเวลาการผสานดังนั้นในกรณีที่ค่าเหมือนกันช่วงจะถูกยุบลงและมีการดำเนินการค้นหาเพียงครั้งเดียวเท่านั้นUsersมิฉะนั้นจะทำการค้นหาสองครั้ง

คุณค่า62คือธงหมายความว่าการแสวงหาควรจะเป็นการแสวงหาความเท่าเทียมกัน

เกี่ยวกับ

ฉันไม่เข้าใจว่าจะสามารถเข้าถึงสิ่งเหล่านี้ได้อย่างไรเมื่อยังไม่ได้เข้าร่วมลูปซ้อนจาก IX_NC_REPUTATION กับทรีย่อยที่มี Expr1010 และ Expr1011

สิ่งเหล่านี้ถูกกำหนดในตัวดำเนินการต่อข้อมูลที่เน้นด้วยสีเหลือง นี่คือด้านนอกของลูปซ้อนซ้อนที่เน้นสีเหลือง ดังนั้นสิ่งนี้จะดำเนินการก่อนที่ไฮไลต์สีเหลืองจะค้นหาที่ด้านในของลูปซ้อนกัน

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

การเขียนซ้ำที่ให้แผนคล้ายกัน (แม้ว่าจะมีช่วงเวลาการรวมที่ถูกแทนที่ด้วยการรวมแบบผสาน) อยู่ด้านล่างในกรณีนี้จะช่วยได้

SELECT DISTINCT D2.UserId
FROM   dbo.Posts p
       CROSS APPLY (SELECT Users.Id AS UserId
                    FROM   (SELECT p.OwnerUserId
                            UNION /*collapse duplicate to single row*/
                            SELECT p.LastEditorUserId) D1(UserId)
                           JOIN Users
                             ON Users.Id = D1.UserId) D2
OPTION (FORCE ORDER) 

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

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

WITH Unpivoted AS
(
SELECT UserId
FROM dbo.Posts
UNPIVOT (UserId FOR U IN (OwnerUserId,LastEditorUserId)) Unpivoted
)
SELECT DISTINCT Users.Id
FROM dbo.Users INNER HASH JOIN Unpivoted
       ON  Users.Id = Unpivoted.UserId
WHERE Users.Reputation = 5

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

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