ไม่ใช้ดัชนี SEEK เว้นแต่ OPTION (RECOMPILE)?


11

(คำถามย้ายจาก SO)

ฉันมีตาราง (ข้อมูลหุ่น) ที่มีดัชนีคลัสเตอร์ประกอบด้วย 2 คอลัมน์:

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

ตอนนี้ฉันเรียกใช้แบบสอบถามทั้งสองนี้:

declare 
@productid int =1 , 
@priceid  int = 1




SELECT productid,
       t.priceID
FROM   Transactions AS t
WHERE  (productID = @productid OR @productid IS NULL)
       AND (priceid = @priceid OR @priceid IS NULL)  


SELECT productid,
       t.priceID
FROM   Transactions AS t
WHERE  (productID = @productid)
       AND (priceid = @priceid)

แผนปฏิบัติการจริงสำหรับทั้งคิวรีคือ:

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

อย่างที่คุณเห็นคนแรกใช้ SCAN ในขณะที่คนที่สองกำลังใช้ SEEK

อย่างไรก็ตาม - การเพิ่มOPTION (RECOMPILE)ลงในแบบสอบถามแรกทำให้แผนการดำเนินการใช้ SEEK ด้วย:

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

เพื่อนที่แชท DBA บอกฉันว่า:

ในการสืบค้นของคุณ @ productid = 1 ซึ่งหมายความว่า (productID = @ productID หรือ @productID IS NULL) สามารถทำให้ง่ายขึ้น (productID = @ productID) อดีตต้องการสแกนเพื่อทำงานกับค่าใด ๆ ของ @productID ซึ่งหลังสามารถใช้การค้นหาได้ ดังนั้นเมื่อคุณใช้ RECOMPILE SQL Server จะดูว่าคุณมีค่าอะไรใน @productID และสร้างแผนการที่ดีที่สุด ด้วยค่าที่ไม่ใช่ค่า null ใน @productID การค้นหาดีที่สุด หากไม่ทราบค่าของ @productID แผนจะต้องมีค่าที่เป็นไปได้ใน @productID ซึ่งจะต้องมีการสแกน ถูกเตือน: OPTION (RECOMPILE) จะบังคับให้คอมไพล์ของแผนทุกครั้งที่คุณเรียกใช้ซึ่งจะเพิ่มมิลลิวินาทีสองสามครั้งในการดำเนินการทุกครั้ง แม้ว่านี่จะเป็นปัญหาหากแบบสอบถามทำงานบ่อยมาก

ยัง:

หาก @productID เป็นโมฆะคุณจะค้นหาว่ามูลค่าเท่าใด คำตอบ: ไม่มีอะไรให้ค้นหา ค่าทั้งหมดมีคุณสมบัติ

ฉันเข้าใจว่าOPTION (RECOMPILE)กำลังบังคับให้ SQL Server เห็นค่าที่แท้จริงที่พารามิเตอร์มีและดูว่าสามารถดูได้หรือไม่

แต่ตอนนี้ฉันเสียประโยชน์จากการรวบรวมล่วงหน้า

คำถาม

IMHO - SCAN จะเกิดขึ้นเฉพาะเมื่อพารามิเตอร์เป็นโมฆะ
ไม่เป็นไร - ให้ SQL Server สร้างแผนการดำเนินการสำหรับ SCAN
แต่ถ้า SQL Server เห็นว่าฉันเรียกใช้แบบสอบถามนี้หลายครั้งด้วยค่า: 1,1แล้วทำไมมันไม่สร้างแผนปฏิบัติการอีก ANEMER และใช้ SEEK สำหรับสิ่งนั้น?

AFAIK - SQL สร้างแผนการดำเนินการสำหรับเคียวรีที่พบบ่อยที่สุด

  • เหตุใด SQL Server จึงไม่บันทึกแผนการดำเนินการสำหรับ:

    @productid int =1 , @priceid int = 1

(ฉันเรียกใช้หลาย ๆ ครั้งด้วยค่าเหล่านั้น)

  • เป็นไปได้หรือไม่ที่จะบังคับให้ SQL รักษาแผนการดำเนินการนั้น (ซึ่งใช้ SEEK) - สำหรับการเรียกใช้ในอนาคต?

สร้างตารางสคริปต์ + ข้อมูลทั้งหมด


2
ขอให้เรายังคงอภิปรายนี้ในการแชท
ypercubeᵀᴹ

คำตอบ:


10

สรุปประเด็นสำคัญบางส่วนจากการสนทนาในห้องแชทของเรา:


พูดโดยทั่วไปใน SQL Server แคชแผนเดียวสำหรับแต่ละคำสั่ง แผนการที่จะต้องมีความถูกต้องสำหรับทุกค่าพารามิเตอร์ที่เป็นไปได้ในอนาคต

มันไม่ได้เป็นไปได้ที่จะแคชแสวงหาแผนสำหรับการค้นหาของคุณเพราะแผนการที่จะไม่ถูกต้องถ้ายกตัวอย่างเช่น@productidเป็นโมฆะ

ในบางรุ่นในอนาคต SQL Server อาจสนับสนุนแผนเดียวที่เลือกระหว่างการสแกนและการค้นหาแบบไดนามิกขึ้นอยู่กับค่าพารามิเตอร์รันไทม์ แต่นั่นไม่ใช่สิ่งที่เรามีในปัจจุบัน

คลาสปัญหาทั่วไป

ข้อความค้นหาของคุณเป็นตัวอย่างของรูปแบบที่หลากหลายซึ่งเรียกว่าแบบสอบถาม "จับทั้งหมด" หรือ "ค้นหาแบบไดนามิก" มีการแก้ปัญหาต่าง ๆ แต่ละคนมีข้อดีและข้อเสียของตัวเอง ในรุ่นที่ทันสมัยของ SQL Server (2008+) ตัวเลือกหลัก ได้แก่ :

  • IF บล็อก
  • OPTION (RECOMPILE)
  • ใช้ Dynamic SQL sp_executesql

งานที่ครอบคลุมมากที่สุดในหัวข้อน่าจะเป็นโดย Erland Sommarskog ซึ่งรวมอยู่ในการอ้างอิงในตอนท้ายของคำตอบนี้ ไม่มีการหลบหนีจากความซับซ้อนที่เกี่ยวข้องดังนั้นจำเป็นต้องลงทุนเวลาในการลองตัวเลือกแต่ละตัวเพื่อทำความเข้าใจการแลกเปลี่ยนในแต่ละกรณี

IF บล็อก

เมื่อต้องการแสดงIFโซลูชันบล็อกสำหรับกรณีและปัญหาเฉพาะในคำถาม:

IF @productid IS NOT NULL AND @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid
        AND T.priceID = @priceid;
END;
ELSE IF @productid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid;
END;
ELSE IF @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.priceID = @priceid;
END;
ELSE
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T;
END;

นี้มีคำสั่งแยกต่างหากสำหรับกรณีที่เป็นโมฆะหรือไม่เป็นโมฆะทั้งสี่กรณีสำหรับพารามิเตอร์ทั้งสอง (หรือตัวแปรท้องถิ่น) ดังนั้นจึงมีสี่แผน

มีปัญหาที่อาจเกิดขึ้นกับการดมกลิ่นพารามิเตอร์ซึ่งอาจต้องการOPTIMIZE FORคำใบ้ในการสืบค้นแต่ละข้อ โปรดดูส่วนการอ้างอิงเพื่อสำรวจประเภทย่อยเหล่านี้

คอมไพล์

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

SELECT
    T.productID,
    T.priceID
FROM dbo.Transactions AS T
WHERE
    (T.productID = @productid OR @productid IS NULL)
    AND (T.priceID = @priceid OR @priceid IS NULL)
OPTION (RECOMPILE);

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

อ่านเพิ่มเติม

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