เหตุใดจึงต้องใช้คำสั่ง INCLUDE เมื่อสร้างดัชนี?


431

ในขณะที่เรียนสำหรับการสอบ 70-433 ผมสังเกตเห็นว่าคุณสามารถสร้างดัชนีครอบคลุมในหนึ่งในสองวิธีต่อไปนี้

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

-- หรือ --

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

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

คำตอบ:


363

หากคอลัมน์ไม่ได้อยู่ในWHERE/JOIN/GROUP BY/ORDER BYแต่จะอยู่ในรายการคอลัมน์ในส่วนSELECTคำสั่งเท่านั้น

INCLUDEข้อเพิ่มข้อมูลที่ระดับต่ำสุด / ใบมากกว่าในต้นไม้ดัชนี สิ่งนี้ทำให้ดัชนีมีขนาดเล็กลงเพราะไม่ได้เป็นส่วนหนึ่งของแผนภูมิ

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

บทความ MSDN อื่นพร้อมตัวอย่างที่ใช้งานได้


7
ดังนั้นนี่จะเป็นเทคนิคในการสร้างดัชนีที่มีราคาถูกกว่าหรือไม่?
JMarsch

3
@gbn คุณจะอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับประโยคนี้และอธิบายว่าทำไมมันหมายถึงการรวมประโยคไม่เป็นประโยชน์สำหรับการเรียงลำดับ ฯลฯ : "ส่วนคำสั่ง INCLUDE เพิ่มข้อมูลที่ระดับต่ำสุด / ลีฟมากกว่าในแผนผังดัชนี สิ่งนี้ทำให้ดัชนีมีขนาดเล็กลงเพราะไม่ได้เป็นส่วนหนึ่งของต้นไม้ "
Tola Odejayi

4
@JMarsch: ขอโทษสำหรับการตอบกลับช้า แต่ใช่นี่คือสิ่งที่มันเป็น
gbn

10
@Tola Odejayi: คอลัมน์ INCLUDE ไม่ใช่คอลัมน์สำคัญในดัชนีดังนั้นจึงไม่ได้เรียงลำดับ สิ่งนี้ทำให้พวกเขามักจะไม่เป็นประโยชน์สำหรับการเข้าร่วมหรือการเรียงลำดับ และเพราะพวกเขาเป็นคอลัมน์ที่ไม่สำคัญพวกเขาไม่ได้นั่งอยู่ในโครงสร้าง B ต้นไม้ทั้งหมดเช่นคอลัมน์คีย์
GBN

4
ในขณะที่นี่เป็นคำตอบที่ได้รับการยอมรับมากที่สุด แต่ฉันคิดว่าจำเป็นต้องมีคำอธิบายเพิ่มเติมถ้าหากมีข้อสงสัยบางข้อคอลัมน์จะเป็นส่วนหนึ่งของคำตอบบางข้อSELECT? \
Chisko

215

คุณจะใช้ INCLUDE เพื่อเพิ่มคอลัมน์หนึ่งคอลัมน์ขึ้นไปในระดับลีฟของดัชนีที่ไม่ทำคลัสเตอร์หากทำเช่นนี้คุณสามารถ "ครอบคลุม" คำสั่งของคุณ

ลองนึกภาพคุณจำเป็นต้องค้นหา ID ของพนักงาน ID แผนกและนามสกุล

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

หากคุณมีดัชนีที่ไม่ทำคลัสเตอร์ใน (EmployeeID, DepartmentID) เมื่อคุณค้นหาพนักงานสำหรับแผนกที่กำหนดตอนนี้คุณต้องทำ "การค้นหาบุ๊กมาร์ก" เพื่อรับระเบียนพนักงานเต็มจริงเพียงรับคอลัมน์นามสกุล . ซึ่งอาจมีราคาแพงในแง่ของประสิทธิภาพหากคุณพบพนักงานจำนวนมาก

หากคุณรวมนามสกุลนั้นไว้ในดัชนีของคุณ:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

ข้อมูลทั้งหมดที่คุณต้องการมีอยู่ในระดับลีฟของดัชนีที่ไม่ทำคลัสเตอร์ เพียงแค่ค้นหาในดัชนีที่ไม่คลัสเตอร์และค้นหาพนักงานของคุณสำหรับแผนกที่ระบุคุณมีข้อมูลที่จำเป็นทั้งหมดและการค้นหาบุ๊กมาร์กสำหรับพนักงานแต่ละคนที่พบในดัชนีนั้นไม่จำเป็นอีกต่อไป -> คุณประหยัดเวลาได้มาก

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


25
คุณแน่ใจหรือว่าคุณใช้ดัชนีนี้ ทำไมต้อง EmployeeID คุณต้องการ DepartmentID ในคอลัมน์สำคัญ ๆ หรือไม่ คุณได้รับการยกมาที่นี่ในฐานะผู้มีสิทธิ์: stackoverflow.com/q/6187904/27535
gbn

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

15
ก่อนอื่นดัชนีพนักงาน (EmployeeID, DepartmentID) จะไม่ถูกใช้เพื่อกรอง DepartmentID = 5 เพราะคำสั่งซื้อไม่ตรง
AnandPhadke

29

การอภิปรายนี้หายไปในจุดสำคัญ: คำถามไม่ใช่ว่า "ไม่ใช่คีย์คอลัมน์" จะดีกว่าที่จะรวมเป็นดัชนี -columns หรือรวม -columns

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

  1. ใช้ดัชนีใน id1, id2 ... idN เพียงอย่างเดียวหรือ
  2. ใช้ดัชนีบน id1, id2 ... idN plus รวม col1, col2 ... colN

โดยที่: id1, id2 ... idN เป็นคอลัมน์ที่ใช้บ่อยในข้อ จำกัด และ col1, col2 ... colN เป็นคอลัมน์ที่เลือกบ่อยครั้ง แต่โดยทั่วไปจะไม่ใช้ในข้อ จำกัด

(ตัวเลือกที่จะรวมคอลัมน์ทั้งหมดเหล่านี้เป็นส่วนหนึ่งของดัชนีคีย์เป็นเพียงโง่เสมอ (เว้นแต่พวกเขาจะใช้ในข้อ จำกัด ) - ทำให้มันจะมีราคาแพงกว่าที่จะรักษาเนื่องจากดัชนีจะต้องปรับปรุงและเรียงลำดับแม้เมื่อ "คีย์" ยังไม่เปลี่ยน)

ใช้ตัวเลือกที่ 1 หรือ 2

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

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

จำนวนแถวเฉลี่ยต่อค่าเดียวกันในคีย์ (id1, id2 ... idN) ก็มีความสำคัญเช่นกัน

ขอให้สังเกตว่าถ้าคอลัมน์ - ที่จะถูกเพิ่มเป็นรวมคอลัมน์ของดัชนี - ใช้ในการ จำกัด : ตราบใดที่ดัชนีดังกล่าวสามารถนำมาใช้ (ขึ้นอยู่กับข้อ จำกัด กับดัชนีที่สำคัญ -columns) - แล้ว SQL Server คือการจับคู่ การ จำกัด คอลัมน์กับดัชนี (leaf-node-values) แทนการใช้วิธีที่มีราคาแพงรอบ ๆ ตาราง


18

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


7

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


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

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

5

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

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


3

เหตุผลหนึ่งที่ชอบINCLUDEมากกว่าคอลัมน์หลักหากคุณไม่ต้องการคอลัมน์นั้นในคีย์คือเอกสารประกอบ ทำให้การพัฒนาดัชนีง่ายขึ้นในอนาคต

พิจารณาตัวอย่างของคุณ:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

ดัชนีนั้นดีที่สุดหากคำค้นหาของคุณมีลักษณะดังนี้:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

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

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

สมมติว่านี่ไม่ใช่กรณีและเรามีcol2อยู่ในINCLUDEclause เนื่องจากไม่มีประโยชน์ที่จะมีในส่วนต้นไม้ของดัชนี

กรอไปข้างหน้าบางปี

คุณต้องปรับแต่งคำถามนี้:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

เพื่อเพิ่มประสิทธิภาพการค้นหานั้นดัชนีต่อไปนี้จะดีมาก:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

หากคุณตรวจสอบว่าคุณมีดัชนีใดในตารางนั้นดัชนีก่อนหน้านี้ของคุณอาจยังอยู่ที่นั่น:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

ตอนนี้คุณรู้แล้วCol2และCol3ไม่ได้เป็นส่วนหนึ่งของแผนภูมิดัชนีและไม่ได้ใช้เพื่อ จำกัด ช่วงดัชนีการอ่านหรือสำหรับการเรียงลำดับแถว ค่อนข้างปลอดภัยที่จะเพิ่มanother_columnไปยังจุดสิ้นสุดของส่วนสำคัญของดัชนี (หลังจากcol1) มีความเสี่ยงเล็กน้อยที่จะทำลายอะไร:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

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

หากคุณจะมีดัชนีโดยไม่ต้องINCLUDEคุณจะไม่สามารถรู้ว่าสิ่งที่คุณจะสอบถามทำลายโดยการเพิ่มทันทีหลังจากanother_colCol1

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

จะเกิดอะไรขึ้นถ้าคุณเพิ่มanother_colระหว่างCol1และCol2? ข้อความค้นหาอื่น ๆ จะประสบหรือไม่

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

ในการตอบคำถามของคุณ:

คุณจะแนะนำแนวทางใดในการพิจารณาว่าจะสร้างดัชนีการครอบคลุมโดยมีหรือไม่มีข้อ INCLUDE หรือไม่

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

หากการเพิ่มคอลัมน์ในคีย์ดัชนีจะก่อให้เกิดประโยชน์เพิ่มเติม (เช่นสำหรับorder byหรือเพราะมันสามารถ จำกัด ช่วงดัชนีการอ่าน) ให้เพิ่มลงในคีย์

คุณสามารถอ่านการสนทนาที่ยาวขึ้นเกี่ยวกับเรื่องนี้ได้ที่นี่:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes


2

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

ในอีกทางหนึ่งการกำหนดดัชนีใน StoreID และรวมถึง LastName, FirstName, คอลัมน์ DOB จะช่วยให้คุณในสาระสำคัญทำสองกริยาดัชนีดัชนีใน StoreID แล้วหากริยาในคอลัมน์ใด ๆ รวม สิ่งนี้จะช่วยให้คุณครอบคลุมการเรียงสับเปลี่ยนการค้นหาที่เป็นไปได้ทั้งหมดตราบใดที่มันเริ่มต้นด้วย StoreID

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