กฎที่แข็งและเร็วสำหรับการรวมคอลัมน์ในดัชนี


38

มีกฎใด ๆ ที่ยากและรวดเร็วในการตัดสินใจว่าควรจะใส่คอลัมน์ใดและเรียงตามลำดับในรวมในดัชนีที่ไม่ทำคลัสเตอร์ ฉันเพิ่งอ่านบทความนี้https://stackoverflow.com/questions/1307990/why-use-the-include-clause-when-creating-an-index และฉันพบว่าสำหรับแบบสอบถามต่อไปนี้:

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

โปสเตอร์แนะนำให้สร้างดัชนีแบบนี้:

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

ที่นี่คำถามของฉันมาทำไมเราไม่สามารถสร้างดัชนีเช่นนี้

CREATE NONCLUSTERED INDEX NC_EmpDep 
      ON Employee( EmployeeID, DepartmentID, LastName)

หรือ

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

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


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

Ryk ส่วนตัวฉันพบว่าโพสต์นี้มีประโยชน์
Jason Young

ฉันพบว่าคำถามนี้มีประโยชน์เช่นกัน ลองเน้นคำถามที่ดีและคำตอบที่ดีแทนการสะกดรอยตามคน ....
Volvox

คำตอบ:


47

คำแนะนำดัชนีนั้นโดย marc_s นั้นผิด ฉันได้เพิ่มความคิดเห็น (และมันก็เป็นคำตอบที่ฉันยอมรับเช่นกัน!)

ดัชนีสำหรับแบบสอบถามนี้จะเป็น

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

โดยทั่วไปดัชนีจะเป็น

CREATE INDEX <name> ON <table> (KeyColList) INCLUDE (NonKeyColList)

ที่ไหน:

  • KeyColList = คอลัมน์สำคัญ = ใช้สำหรับการ จำกัด แถวและการประมวลผล
    WHERE, JOIN, ORDER BY, GROUP BY ฯลฯ
  • NonKeyColList = คอลัมน์ที่ไม่ใช่คีย์ = ใช้ใน SELECT และการรวม (เช่น SUM (col)) หลังจากการเลือก / ข้อ จำกัด

+1 - ฉันเห็นด้วย (ดูคำตอบของฉัน) ว่าดัชนีตัวอย่างใน OP ไม่มีประโยชน์สำหรับการค้นหา!
JNK

ที่ดี! อีกสิ่งหนึ่งที่จะเป็นคำสั่งของ KeyColList และ NonKeyColList คุณสามารถอธิบายด้วยตัวอย่างของฉันได้ไหม สมมติว่าแบบสอบถามของฉันคือ SELECT EmployeeID, DepartmentID, นามสกุลจาก EmployeeWHERE DepartmentID = 5, StateID = 4 ดัชนีนี้ควรเป็นอย่างไร

@Rocky - NonKeyColListคำสั่งไม่สำคัญ KeyColListคำสั่งซื้อควรอยู่ในลำดับความถี่ที่คุณคาดว่าจะใช้ในการค้นหา ดูบันทึกย่อของฉันเกี่ยวกับคำตอบของฉันด้านล่าง แต่เหมือนLast Name, First Name, Middile Initialในสมุดโทรศัพท์ คุณต้องการฟิลด์แรกเพื่อค้นหาฟิลด์ที่สอง
JNK

@gbn เราต้องการ EmployeeID ในรายการรวมหรือไม่? เช่นถ้าเรามีดัชนีคลัสเตอร์ในคอลัมน์ EmployeeID และด้านบนของสิ่งนี้ถ้าเราสร้างดัชนี nonclustered ในคอลัมน์ DeptId ดังนั้นดัชนี NonClustered แล้วมีการอ้างอิงถึงคีย์การจัดกลุ่มซึ่งรวมอยู่ในโครงสร้างดัชนี NonClustered รวมถึงคีย์การจัดกลุ่มในรายการ INCLUDE ไม่ได้ ' ไม่เพิ่มประโยชน์ใด ๆ
Viswanathan Iyer

1
@ViswanathanIyer จะไม่ถูกเพิ่มอีกสองครั้งในที่เก็บข้อมูลบนดิสก์จริง: SQL Server ตรวจพบสิ่งนี้ ดังนั้นจึงไม่จำเป็น แต่มันทำให้สิ่งต่าง ๆ ชัดเจนขึ้น อย่างไรก็ตามเราไม่รู้จักดัชนีกลุ่มใด ๆ ในคำถามดังนั้นจึงปลอดภัยกว่าที่จะสมมติว่าไม่มี
gbn

19

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

Employee(DepartmentID) INCLUDE (Lastname, EmployeeID)

ดัชนีนี้ไม่ได้ช่วยเลยถ้าแบบสอบถามเปลี่ยนแปลงเล็กน้อยเช่น:

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5 AND LastName = 'Smith'

สิ่งนี้จะต้องมีดัชนี (# 2):

Employee(DepartmentID, LastName) INCLUDE (EmployeeID)

ลองนึกภาพคุณมีพนักงาน 1,000 คนในแผนกที่ 5 โดยใช้ดัชนี # 1 เพื่อหาสมิ ธ ทั้งหมดคุณต้องหาแถว 1,000 ทั้งหมดในแผนกที่ 5 เนื่องจากคอลัมน์ที่รวมอยู่นั้นไม่ได้เป็นส่วนหนึ่งของคีย์ ใช้ดัชนี # 2 คุณสามารถค้นหาโดยตรงไปยังแผนก 5, LastName Smith

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


ในฐานะที่เป็นหมายเหตุด้านมันเป็นมูลค่าชี้ให้เห็นว่าถ้า EmployeeID เป็นคีย์การจัดกลุ่มสำหรับตารางนี้ - สมมติว่าดัชนีคลัสเตอร์ - แล้วคุณไม่จำเป็นต้องรวม EmployeeID - มันมีอยู่ในดัชนีที่ไม่ใช่คลัสเตอร์ทั้งหมดหมายถึงดัชนี # 2 เป็น

Employee(DepartmentID, LastName)

2
+1 สำหรับข้อมูลที่เป็นประโยชน์เพิ่มเติม สำหรับจุดสุดท้ายของคุณฉันทดสอบและใช้ EmployeeID อย่างชัดเจนใน INCLUDE จริง ๆ แล้วจะถูกละเว้น (ขึ้นอยู่กับขนาดของดัชนี) ถ้า EmployeeID เป็นดัชนีคลัสเตอร์ ชัดเจนยิ่งขึ้นแม้ว่าฉันจะคิดและไม่มีข้อเสียที่ว่าง
gbn

1
ฉันเห็นด้วยอย่างยิ่ง - จะดีกว่าเสมอโดยเฉพาะถ้าไม่มีอะไรเลย!

1
ในกรณีที่ ... ฉันหมายถึงฉันได้ทดสอบคีย์คลัสเตอร์ใน INCLUDE (ไม่ใช่ EmployeeID อย่างชัดเจน) และเพิ่มพื้นที่ว่าง ในคอลัมน์สำคัญมันทำ
gbn

@gbn ใช่คีย์คลัสเตอร์จะต้องอยู่ในระดับลีฟของดัชนีซึ่งเป็นที่ที่คอลัมน์ INCLUDE อยู่ การย้ายมันไปไว้ในคีย์ดัชนีนั้นหมายความว่ามันจะมีอยู่ในหน้าเว็บที่ไม่ใช่ใบไม้เช่นกัน สิ่งนี้จะส่งผลให้มีการขยายตัวเล็กน้อย แต่ไม่มากนัก (ในหน้าระดับกลางคุณจะต้องเพิ่มอีก 4 ไบต์ต่อหน้าระดับลีฟโดยสมมติว่ามีจำนวนเต็ม)

นี่คือคำตอบที่ยอดเยี่ยมซึ่งรวมถึงเอฟเฟกต์บางอย่างที่อธิบายไว้ในบทความนี้: sqlperformance.com/2014/07/sql-indexes/ …หากแบบสอบถามของคุณเปลี่ยนแปลงดังนั้นข้อกำหนดของดัชนีของคุณก็เช่นกัน คุณอาจจะดีกว่าด้วยคำตอบของ Jim แต่คุณอาจรู้สึกดีขึ้นด้วย @gbn answer
John aka hot2use

7

ฉันไม่แน่ใจว่าคุณได้รับสิ่งแรกอย่างไร สำหรับฉันสำหรับข้อความค้นหานั้นฉันจะใช้:

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

ไม่มี "กฎที่ยากและรวดเร็ว" สำหรับทุกอย่างใน SQL

แต่สำหรับตัวอย่างของคุณฟิลด์เดียวที่ดัชนีจะใช้คือDepartmentIDเนื่องจากอยู่ในส่วนWHEREคำสั่ง

สาขาอื่น ๆ เพียงแค่ต้องสามารถเข้าถึงได้ง่ายจากที่นั่น คุณเลือกตามDepartmentIDนั้นINCLUDEมีเขตข้อมูลเหล่านั้นที่โหนดใบของดัชนี

คุณไม่ต้องการใช้ตัวอย่างอื่นของคุณเพราะพวกเขาจะไม่ทำงานกับดัชนีนี้

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

INCLUDEเขตข้อมูลเช่นหมายเลขโทรศัพท์ที่อยู่ ฯลฯ ข้อมูลอื่น ๆ สำหรับแต่ละรายการในหนังสือเล่มนี้

แก้ไข:

หากต้องการชี้แจงเพิ่มเติมว่าทำไมไม่ใช้:

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

ดัชนีนี้มีประโยชน์ก็ต่อเมื่อคุณมีEmployeeIDหรือทั้งสองอย่าง EmployeeIDและLastNameอยู่ในWHEREข้อของคุณ นี่เป็นสิ่งที่ตรงข้ามกับสิ่งที่คุณต้องการมากสำหรับแบบสอบถามนี้


@ajbeaven นั่นเป็นความจริงซึ่งเป็นเหตุผลว่าทำไมความคิดเห็นที่ฉันใส่ไว้ในการแก้ไขบอกว่าคุณต้องการ EITHER employeeID หรือทั้งสองคอลัมน์
JNK

ขออภัยที่อ่านผิด :(
ajbeaven

0

ฉันคิดว่าคุณอาจยังสามารถใช้ดัชนี (employee_id, department_id) ได้ แต่คุณจะต้องรวมบรรทัด 'จำลอง' ไว้ในวลีที่เช่น: "employee_id = employee_id)

  • มีดัชนีใน (employee_id, Departemnent_id),
  • ต้องค้นหา / จำกัด เฉพาะใน department_id
  • รู้ว่ามันจะไม่ใช้ดัชนีเนื่องจากคำสั่งที่ไม่ถูกต้อง(หรือสิ่งที่มีการเปลี่ยนแปลงได้โดยในขณะนี้และต่อไปนี้ "เคล็ดลับ" เป็นสิ่งจำเป็นอีกต่อไป. ฉันเป็น "Oldy"?)
  • ใช้ tricK "เก่า" หรือไม่

    เลือก * จากพนักงาน EMP
    โดยที่emp.employee_id = emp.employee_id
    และ emp.department_id = 5

(ดังนั้นฉันไม่ได้มุ่งเน้นไปที่ส่วนรวมที่นี่ของ Lastname แต่ใช้คีย์ใช่ / หรือไม่ใช้)

ขอแสดงความนับถือ,

Miguell


2
ไม่นั่นไร้ประโยชน์และไม่มีประสิทธิภาพ
ypercubeᵀᴹ

โดยเฉพาะจะต้องสแกนดัชนีเพื่อค้นหา id พนักงานทุกคนเพื่อค้นหาอินสแตนซ์ทั้งหมดของ department_id 5 หากมีพนักงาน 1,000 คนและ 5 แผนก SQL ต้องค้นหาพนักงาน 1,000 คนเพื่อค้นหาแถวทั้งหมดสำหรับแผนกเฉพาะ
Mark Sowul

ตอนนี้ให้พิจารณากรณีตรงข้าม (ดัชนีอยู่ใน department_id, employee_id) เห็นได้ชัดว่ามันง่ายที่จะหาแผนกเฉพาะในขณะนี้ แต่ยังทราบด้วยว่าในการค้นหาพนักงานคนหนึ่ง SQL ต้องสแกนผ่าน 5 แผนกเพื่อค้นหาแถวทั้งหมดสำหรับพนักงานเฉพาะ
Mark Sowul
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.