การตัดคำค้นหาใน IF EXISTS ทำให้ช้ามาก


16

ฉันมีแบบสอบถามด้านล่าง:

select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)

แบบสอบถามด้านบนเสร็จสมบูรณ์ในสามวินาที

หากเคียวรีด้านบนคืนค่าใด ๆ เราต้องการให้โพรซีเดอร์ที่เก็บไว้เป็น EXIT ดังนั้นฉันจึงเขียนมันใหม่ด้านล่าง:

If Exists(
select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End

อย่างไรก็ตามการดำเนินการนี้ใช้เวลา 10 นาที

ฉันสามารถเขียนข้อความค้นหาด้านบนเหมือนด้านล่างซึ่งเสร็จสมบูรณ์ในเวลาน้อยกว่า 3 วินาที:

  select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End

ปัญหาเกี่ยวกับการเขียนซ้ำข้างต้นคือแบบสอบถามข้างต้นเป็นส่วนหนึ่งของขั้นตอนการจัดเก็บที่ใหญ่กว่าและส่งคืนชุดผลลัพธ์หลายชุด ใน C # เราวนซ้ำแต่ละชุดผลลัพธ์และทำการประมวลผลบางอย่าง

ด้านบนส่งคืนชุดผลลัพธ์ที่ว่างเปล่าดังนั้นถ้าฉันไปด้วยวิธีนี้ฉันต้องเปลี่ยน C # ของฉันและทำการปรับใช้อีกครั้ง

ดังนั้นคำถามของฉันคือ

เหตุใดการใช้เพียงIF EXISTSเปลี่ยนแผนเพื่อใช้เวลามาก

ด้านล่างนี้เป็นรายละเอียดที่อาจช่วยคุณและแจ้งให้เราทราบหากคุณต้องการรายละเอียดใด ๆ :

  1. สร้างตารางและสคริปต์สถิติเพื่อรับแผนเดียวกับของฉัน
  2. แผนปฏิบัติการช้า
  3. แผนปฏิบัติการที่รวดเร็ว

    แผนช้าโดยใช้ Brentozar วางแผนแผน
    รวดเร็วโดยใช้ Brentozar วางแผน

หมายเหตุ:ข้อความค้นหาทั้งสองเหมือนกัน (ใช้พารามิเตอร์) ความแตกต่างเพียงอย่างเดียวคือEXISTS(ฉันอาจทำผิดพลาดบางอย่างในขณะที่ไม่ระบุตัวตน)

สคริปต์การสร้างตารางอยู่ด้านล่าง:

http://pastebin.com/CgSHeqXc - สถิติตารางขนาดเล็ก
http://pastebin.com/GUu9KfpS - สถิติตารางขนาดใหญ่


การสนทนากับคำถามนี้ถูกย้ายไปที่ห้องสนทนานี้แล้ว
Paul White Reinstate Monica

คำตอบ:


18

ตามที่ได้รับการอธิบายโดยพอลไวท์ในโพสต์บล็อกของเขา: ภายในเพิ่มประสิทธิภาพ: เป้าหมายแถวในความลึกEXISTSแนะนำเป้าหมายแถวที่ชอบNESTED LOOPSหรือMERGE JOINมากกว่าHASH MATCH

เป็นตัวอย่างสุดท้ายพิจารณาว่าการรวมกึ่งแบบโลจิคัล (เช่นแบบสอบถามย่อยที่แนะนำกับ EXISTS) แบ่งปันธีมโดยรวม: ควรปรับให้เหมาะสมเพื่อค้นหาแถวแรกที่ตรงกันอย่างรวดเร็ว

ในการสืบค้นของคุณสิ่งนี้เกิดขึ้นเพื่อแนะนำลูปซ้อนกันและลบการขนานซึ่งส่งผลให้แผนช้าลง

ดังนั้นคุณอาจต้องค้นหาวิธีการเขียนแบบสอบถามของคุณใหม่โดยไม่ต้องใช้ข้อมูลNOT EXISTSจากแบบสอบถามของคุณ

คุณอาจไม่สามารถเขียนข้อความค้นหาของคุณใหม่โดยใช้ a LEFT OUTER JOINและตรวจสอบว่าไม่ได้มีแถวในโต๊ะเล็ก ๆ โดยการทดสอบNULL

If EXISTS(
    SELECT databasename
    FROM somedb.dbo.bigtable l
    LEFT JOIN dbo.smalltable c ON c.source = l.source
    WHERE databasename = 'someval'
    AND source <> 'kt'
    AND c.source IS NULL
)

คุณอาจใช้EXCEPTแบบสอบถามได้เช่นกันทั้งนี้ขึ้นอยู่กับจำนวนเขตข้อมูลที่คุณต้องการเปรียบเทียบเช่นนี้

If EXISTS(
   SELECT source
   FROM somedb.dbo.bigtable l
   WHERE databasename = 'someval'
   AND source <> 'kt'

   EXCEPT

   SELECT source
   FROM dbo.smalltable
)

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

คำถาม & คำตอบที่เกี่ยวข้อง: หาก EXISTS ใช้เวลานานกว่าคำสั่ง select แบบฝัง


0

คุณต้องเขียนแบบสอบถามของคุณใหม่โดยใช้การรวมอย่างชัดเจนและระบุการดำเนินการเข้าร่วมที่คุณต้องการใช้ (วนซ้ำแฮชหรือรวม) เช่นนี้

If not exists(
    select databasename 
    from somedb.dbo.bigtable l
    inner hash join dbo.smalltable c 
        on c.source = l.source
where databasename ='someval' and source  <>'kt')
begin
    Raiserror('Source missing',16,1)
    Return
end

เมื่อใช้ EXISTS หรือไม่ใช่ EXISTS SQL Server จะสร้างแผนคิวรีที่มีการดำเนินการ NESTED LOOP โดยสมมติว่าควรข้ามแถวทั้งหมดในชุดที่กำหนดโดยการค้นหาแถวแรกเพื่อให้ตรงกับเงื่อนไข การใช้ HASH JOIN จะช่วยเพิ่มความเร็ว


กว่าคุณจะทดสอบมัน
TheGameiswar

0

ฉันเจอปัญหาเดียวกันฉันพยายามหลีกเลี่ยงการใช้ "EXISTS" และใช้ประโยชน์จากฟังก์ชั่น "COUNT ()" และคำสั่ง "IF ... ELSE"

สำหรับตัวอย่างของคุณลองต่อไปนี้:

IF
(
    SELECT
        COUNT(l.databasename) + 1 AS databasename
    FROM somedb.dbo.bigtable AS l

    WHERE   l.databasename ='someval'
        AND l.[source]  <> 'kt'
        AND NOT EXISTS(SELECT 1 FROM dbo.smalltable AS c WHERE c.[source]=l.[source])
) > 1 --Acts like EXISTS
BEGIN
    RAISERROR('Source missing', 16, 1)
RETURN
END

เหตุผลที่ฉันเพิ่ม "+ 1" ในการนับคือเพื่อให้ฉันสามารถใช้ "> 1" ในเงื่อนไข IF การใช้ "> 0" หรือ "<> 0" จะทำให้การสืบค้นใช้ลูปซ้อนซ้อนแทนแฮช การจับคู่. ยังไม่ได้ตรวจสอบว่าทำไมสิ่งที่เกิดขึ้นอย่างแน่นอนจะน่าสนใจเพื่อค้นหาว่าทำไม

หวังว่าจะช่วย!

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