ทำไมส่วนคำสั่ง WHERE ของฉันจึงได้รับประโยชน์จากคอลัมน์ "รวม"


12

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

ฉันมีคำจำกัดความนี้:

CREATE TABLE [dbo].[JobItems] (
    [ItemId]             UNIQUEIDENTIFIER NOT NULL,
    [ItemState]          INT              NOT NULL,
    [ItemPriority]       INT NOT NULL,
    [CreationTime]       DATETIME         NULL DEFAULT GETUTCDATE(),
    [LastAccessTime]     DATETIME         NULL DEFAULT GETUTCDATE(),
     -- other columns
 );

 CREATE UNIQUE CLUSTERED INDEX [JobItemsIndex]
    ON [dbo].[JobItems]([ItemId] ASC);
 GO

CREATE INDEX [GetItemToProcessIndex]
    ON [dbo].[JobItems]([ItemState], [ItemPriority], [CreationTime])
    INCLUDE (LastAccessTime);
GO

และแบบสอบถามนี้:

UPDATE TOP (150) JobItems 
SET ItemState = 17 
WHERE 
    ItemState IN (3, 9, 10)
    AND LastAccessTime < DATEADD (day, -2, GETUTCDATE()) 
    AND CreationTime < DATEADD (day, -2, GETUTCDATE());

ฉันตรวจสอบแผนจริงและมีเพียงหนึ่งดัชนีที่ค้นหาพร้อมคำกริยาเหมือนกับในWHERE- ไม่มี "การค้นหาบุ๊กมาร์ก" เพิ่มเติมเพื่อเรียกดูLastAccessTimeแม้ว่าจะมีเพียง "รวม" ลงในดัชนีไม่ใช่ส่วนหนึ่งของดัชนี

ฉันดูเหมือนว่าพฤติกรรมนี้ขัดแย้งกับกฎที่คอลัมน์นั้นต้องเป็นส่วนหนึ่งของดัชนีและไม่ใช่แค่ "รวมไว้"

พฤติกรรมที่ฉันสังเกตเห็นนั้นถูกต้องหรือไม่? ฉันจะทราบล่วงหน้าได้อย่างไรหากฉันได้WHEREรับประโยชน์จากคอลัมน์ที่รวมอยู่หรือต้องการให้คอลัมน์เป็นส่วนหนึ่งของดัชนี


มันยังคงสามารถค้นหาตามItemStateค่าอย่างไรก็ตาม Seek จะไม่มีประสิทธิภาพเหมือนกับว่าดัชนีของคุณมีโครงสร้างดังนี้(ItemState, CreationTime, LastAccessTime)
Mark Sinkinson

1
@ MarkSinkinson หรือเพียง(ItemState, CreationTime) INCLUDE (LastAccessTime)
ypercubeᵀᴹ

@sharptooth คำตอบที่เชื่อมโยงซึ่งคุณไม่ได้บอกว่า ("ยกเว้นกรณีที่ดัชนีถูกสร้างขึ้นเหนือคอลัมน์ที่ใช้ในการ จำกัด การสืบค้นจะไม่ได้รับประโยชน์จากดัชนี") มันบอกว่าดัชนีบน(a,b)นั้นไม่ดีที่สุดสำหรับแบบสอบถามSELECT a FROM t WHERE b=5;และดัชนี(b) INCLUDE (a)นั้นดีกว่ามาก
ypercubeᵀᴹ

คำตอบ:


9

เพรดิเคตของคุณแตกต่างจาก Seek Predicate ของคุณ

Seek Predicate ใช้เพื่อค้นหาข้อมูลที่จัดเรียงในดัชนี ในกรณีนี้มันจะทำการค้นหาสามครั้งครั้งละหนึ่งรายการสำหรับแต่ละรัฐที่คุณสนใจนอกจากนั้นข้อมูลนั้นอยู่ในลำดับ ItemPriority ดังนั้นจึงไม่มีการดำเนินการ "แสวงหา" อีกต่อไป

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

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

คุณสามารถดูเนื้อหาที่ฉันเขียนเกี่ยวกับเรื่องนี้ได้ใน Sargability ตรวจสอบเซสชันที่ SQLBits โดยเฉพาะที่http://bit.ly/Sargability

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

นอกจากนี้ยังเป็นที่น่าสังเกตว่าเนื่องจากในส่วนคำสั่งใน ItemState ข้อมูลที่ถูกส่งไปทางซ้ายเป็นจริงในลำดับ ItemState ไม่ใช่ในลำดับ ItemPriority ดัชนีคอมโพสิตบน ItemState ตามด้วยหนึ่งในวันที่ (เช่น (ItemState, LastAccessTime)) สามารถใช้สาม Seeks ได้ (สังเกตว่า Seek Predicate แสดงการค้นหาสามครั้งภายในตัวดำเนินการ Seek หนึ่งตัว) แต่ละระดับมีข้อมูลสองระดับ ยังอยู่ในลำดับ ItemState (เช่น ItemState = 3 และ LastAccessTime น้อยกว่าบางอย่างจากนั้น ItemState = 9 และ LastAccessTime น้อยกว่าบางอย่างแล้ว ItemState = 10 และ LastAccessTime น้อยกว่าบางอย่าง)

ดัชนีใน (ItemState, LastAccesTime, CreationTime) จะไม่มีประโยชน์มากกว่าหนึ่งใน (ItemState, LastAccessTime) เนื่องจากระดับ CreationTime มีประโยชน์เฉพาะเมื่อ Seek ของคุณมีเฉพาะชุด ItemState และ LastAccessTime เท่านั้นไม่ใช่ช่วง เช่นเดียวกับวิธีที่สมุดโทรศัพท์ไม่ได้อยู่ในลำดับ FirstName หากคุณสนใจในนามสกุลที่เริ่มต้นใน F

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

ตามคำศัพท์ที่เหลือ - เป็นคำศัพท์ของฉันสำหรับทรัพย์สินของ Seek การรวมเข้าร่วมเรียกอย่างชัดเจนว่ามันเทียบเท่ากับเพรดิเคตที่เหลือและการแข่งขันแฮชเรียกมันว่าโพรบที่เหลือ (ซึ่งคุณอาจได้รับจาก TSA ถ้าคุณจับคู่แฮช) แต่ในการแสวงหาพวกเขาเรียกว่าเพรดิเคตซึ่งทำให้ดูแย่น้อยลงกว่าเดิม


3

GetItemToProcessIndex ไม่ seekable ItemState + LastAccessTime + CreationTimeอย่างเต็มที่เพราะที่ข้อของคุณอยู่ใน คอลัมน์ที่จัดทำดัชนีและส่วนคำสั่งที่ตรงกับที่ไม่สมบูรณ์แบบ

หากคุณสร้างดัชนีการครอบคลุมItemState + LastAccessTime + CreationTimeสำหรับแต่ละการแข่งขันที่คุณได้รับจาก GetItemToProcessIndex คุณจะได้รับค่าของคีย์หลักของคุณ (ItemId) เพียงเพื่อให้แน่ใจว่าวันที่ 2 เป็นการแข่งขัน

นี่คือทั้งหมดที่คุณต้องการแล้วข้ามไปยังตำแหน่งของแถวบนหน้าและอัปเดต

ด้วยดัชนีปัจจุบันของคุณมันอาจช่วยให้เซิร์ฟเวอร์ค้นหาแถวด้วย ItemState ที่คุณต้องการ แต่ก็ยังต้องอ่านทั้งหมดจากดัชนีเพื่อหาการจับคู่ที่ถูกต้องใน LastAccessTime + CreationTime ขึ้นอยู่กับภาคแสดงวันที่และขนาดของชุดการจับคู่และสิ่งที่ต้องแยกออกมันอาจส่งผลให้ IO มากกว่าดัชนีที่ครอบคลุมอย่างสมบูรณ์ใน 3 คอลัมน์เท่านั้นซึ่งจะค้นหา ItemState และคอลัมน์ที่สอง (วันที่จัดทำดัชนีวันที่ 1) . วันที่ที่สองในการจัดทำดัชนีสามารถรวมได้ ไม่ควรทำดัชนีคอลัมน์พิเศษระหว่าง 3 สิ่งเหล่านี้แม้ว่าจะเป็นคอลัมน์ที่ 4 ก็ได้ (ดูคำตอบของ Rob เกี่ยวกับคอลัมน์เพิ่มเติม)

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