Aturan keras dan cepat untuk menyertakan kolom dalam indeks

38

Apakah ada aturan yang keras dan cepat untuk memutuskan kolom apa dan urutan mana yang harus dimasukkan dalam indeks Non-clustered. Saya baru saja membaca posting ini https://stackoverflow.com/questions/1307990/why-use-the-include-clause-when-creating-an-index dan saya menemukan itu untuk kueri berikut:

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

Poster menyarankan untuk membuat indeks seperti ini:

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

inilah pertanyaan saya mengapa kita tidak dapat membuat indeks seperti ini

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

atau

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

dan apa yang menyebabkan poster memutuskan untuk memasukkan kolom LastName. Kenapa tidak kolom lain? dan bagaimana memutuskan dalam urutan apa kita harus menyimpan kolom di sana?

Komunitas
sumber
3
TERMASUK biasanya memiliki bidang yang Anda butuhkan SETELAH catatan telah ditemukan, menghemat perjalanan pulang-pergi untuk mendapatkan lebih banyak data. Urutan bidang dalam INCLUDE tidak penting.
Jimbo
Ryk, secara pribadi saya menemukan posting ini bermanfaat.
Jason Young
Saya menemukan pertanyaan ini bermanfaat juga. Mari kita fokus pada pertanyaan yang bagus dan jawaban yang baik alih-alih menguntit individu ....
Volvox

Jawaban:

47

Saran indeks oleh marc_s salah. Saya telah menambahkan komentar. (Dan jawaban saya juga diterima!)

Indeks untuk kueri ini adalah

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

Indeks biasanya

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

Dimana:

  • KeyColList = Kolom kunci = digunakan untuk pembatasan dan pemrosesan baris
    DI MANA, BERGABUNG, MEMESAN OLEH, GROUP BY, dll.
  • NonKeyColList = Kolom non-kunci = digunakan dalam SELECT dan agregasi (mis. SUM (col)) setelah pemilihan / pembatasan
gbn
sumber
+1 - Saya setuju (lihat ans saya) bahwa indeks sampel di OP tidak berharga untuk kueri!
JNK
Besar! hanya satu hal lagi yang akan menentukan urutan KeyColList dan NonKeyColList. Bisakah Anda jelaskan dengan contoh saya? Misalkan sekarang permintaan saya adalah SELECT EmployeeID, DepartmentID, LastName FROM EmployeeWHERE DepartmentID = 5, StateID = 4 Bagaimana dia bisa menjadi indeks sekarang?
@ Rocky - NonKeyColListpesanan tidak masalah. KeyColListurutan harus dalam urutan frekuensi yang Anda harapkan akan digunakan dalam permintaan. Lihat catatan saya pada jawaban saya di bawah, tetapi itu seperti Last Name, First Name, Middile Initialdi buku telepon. Anda perlu bidang pertama untuk menemukan bidang kedua.
JNK
@ gbn Apakah kita benar-benar mengharuskan EmployeeID di daftar sertakan? As Jika kita memiliki indeks berkerumun pada kolom EmployeeID dan di atas ini jika kita membuat indeks nonclustered pada kolom DeptId maka indeks NonClustered telah memiliki referensi untuk kunci pengelompokan yang termasuk dalam struktur Indeks NonClustered, termasuk kunci pengelompokan dalam daftar INCLUDE tidak t menambahkan manfaat apa pun.
Viswanathan Iyer
1
@ViswanathanIyer tidak akan ditambahkan dua kali ke penyimpanan di-disk sebenarnya: SQL Server mendeteksi ini. Jadi itu tidak diperlukan tetapi itu membuat segalanya menjadi lebih jelas. Namun, kami tidak tahu adanya indeks berkerumun dalam pertanyaan sehingga lebih aman untuk tidak menganggapnya.
gbn
19

JNK dan gbn telah memberikan jawaban yang bagus, tetapi juga layak mempertimbangkan gambaran besarnya - tidak hanya berfokus pada satu permintaan. Meskipun kueri khusus ini mungkin mendapat manfaat dari indeks (# 1):

Employee(DepartmentID) INCLUDE (Lastname, EmployeeID)

Indeks ini tidak membantu sama sekali jika permintaan sedikit berubah, seperti:

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

Ini membutuhkan indeks (# 2):

Employee(DepartmentID, LastName) INCLUDE (EmployeeID)

Bayangkan Anda memiliki 1.000 karyawan di Departemen 5. Dengan menggunakan indeks # 1, untuk menemukan semua Smiths, Anda harus mencari semua 1.000 baris di Departemen 5, karena kolom yang disertakan bukan bagian dari kunci. Dengan menggunakan indeks # 2, Anda dapat mencari langsung ke Departemen 5, LastName Smith.

Dengan demikian, Indeks # 2 lebih bermanfaat untuk melayani berbagai pertanyaan yang lebih luas - tetapi biayanya adalah kunci indeks yang lebih membengkak, yang akan membuat halaman indeks yang tidak berdaun lebih besar. Setiap sistem akan berbeda, jadi tidak ada aturan umum di sini.


Sebagai catatan, ada baiknya menunjukkan bahwa jika EmployeeID adalah kunci pengelompokan untuk tabel ini - dengan asumsi indeks berkerumun - maka Anda tidak perlu memasukkan EmployeeID - itu ada di semua indeks non-cluster, artinya indeks # 2 bisa saja menjadi

Employee(DepartmentID, LastName)

sumber
2
+1 untuk info lebih bermanfaat. Untuk poin terakhir Anda, saya menguji ini dan penggunaan eksplisit EmployeeID di INCLUDE sebenarnya diabaikan (berdasarkan ukuran indeks) jika EmployeeID adalah indeks berkerumun. Ini lebih jelas meskipun saya pikir dan tidak ada kekurangan ruang.
gbn
1
Saya sangat setuju - itu selalu lebih baik untuk menjadi eksplisit, terutama jika tidak ada biaya!
1
Untuk berjaga-jaga ... Maksud saya saya telah menguji kunci berkerumun di TERMASUK (bukan EmployeeID secara eksplisit) dan tidak menambah ruang. Di kolom kunci itu.
gbn
@ gbn Ya, kunci kluster hanya perlu berada di tingkat daun indeks, yang merupakan tempat kolom TERMASUK berada. Memindahkannya ke kunci indeks berarti itu akan ada di halaman juga. Ini akan menghasilkan sedikit mengasapi, tetapi bukan jumlah yang mengerikan (pada halaman tingkat menengah, Anda akan menambahkan 4 byte per halaman tingkat daun, dengan asumsi Integer).
Ini adalah jawaban yang bagus yang mencakup beberapa efek yang dijelaskan dalam artikel ini: sqlperformance.com/2014/07/sql-indexes/... Jika permintaan Anda berubah, maka lakukan juga persyaratan indeks Anda. Anda mungkin lebih baik dengan jawaban Jim tetapi Anda mungkin lebih baik dengan jawaban @gbn.
John aka hot2use
7

Saya tidak yakin bagaimana Anda mendapatkan yang pertama. Bagi saya, untuk permintaan itu, saya akan menggunakan:

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

Tidak ada "Aturan keras dan cepat" untuk hampir semua hal dalam SQL.

Tetapi, untuk contoh Anda, satu-satunya bidang yang akan digunakan indeks adalah DepartmentIDkarena berada dalam WHEREklausa.

Kolom lain hanya perlu mudah diakses dari sana. Anda memilih berdasarkan pada DepartmentIDkemudian INCLUDEmemiliki bidang-bidang di simpul daun indeks.

Anda tidak ingin menggunakan contoh Anda yang lain karena mereka tidak akan berfungsi untuk indeks ini.

Pikirkan indeks seperti buku telepon. Sebagian besar buku telepon dipesan dengan Nama Belakang, Nama Depan, Nama Awal Menengah. Jika Anda tahu nama depan seseorang, tetapi bukan nama belakangnya, buku telepon itu tidak berguna karena Anda tidak dapat mencari nama depan berdasarkan urutan indeks buku telepon itu.

The INCLUDEbidang seperti nomor telepon, alamat, dll informasi lainnya untuk setiap entri dalam buku.

EDIT:

Untuk lebih memperjelas mengapa tidak menggunakan:

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

Indeks ini hanya berguna jika Anda memiliki EmployeeIDatau KEDUA EmployeeID dan LastNamedalam WHEREklausa Anda . Ini cukup OPPOSITE dari apa yang Anda butuhkan untuk permintaan ini.

JNK
sumber
@ajbeaven itu benar, itulah sebabnya komentar yang saya masukkan di edit mengatakan Anda perlu EIDHER employeeID atau kedua kolom.
JNK
durr sorry misread :(
ajbeaven
0

Saya pikir Anda mungkin masih dapat menggunakan indeks (employee_id, department_id), tetapi Anda harus memasukkan baris 'dummy' dalam frasa tempat, seperti: "employee_id = employee_id)

  • memiliki indeks pada (employee_id, departemnent_id),
  • harus mencari / membatasi hanya pada department_id
  • mengetahui itu tidak akan menggunakan indeks sejak urutan yang salah (atau hal-hal telah berubah sekarang, dan "trik" berikut tidak lagi diperlukan. Saya seorang "oldy"?) .
  • Gunakan trik "lama"?

    pilih * dari emp Karyawan di
    mana emp.employee_id = emp.employee_id
    dan emp.department_id = 5

(Jadi saya tidak fokus pada bagian sertakan di sini Nama belakang, tetapi pada kunci ya / tidak digunakan.)

Salam Hormat,

Miguell

Miguel Leeuwe
sumber
2
Tidak, itu tidak berguna dan tidak efisien.
ypercubeᵀᴹ
Secara khusus, masih harus melakukan pemindaian indeks untuk mencari setiap id karyawan untuk menemukan semua instance dari department_id 5. Jika ada 1000 karyawan dan 5 departemen, SQL harus memeriksa semua 1000 karyawan untuk menemukan semua baris untuk departemen tertentu.
Mark Sowul
Sekarang pertimbangkan hal sebaliknya (indeks ada di department_id, employee_id). Jelas sangat mudah untuk menemukan departemen tertentu sekarang, tetapi juga perhatikan bahwa untuk menemukan karyawan tertentu, SQL hanya perlu memindai melalui 5 departemen untuk menemukan semua baris untuk karyawan tertentu.
Mark Sowul