Mengapa menggunakan klausa INCLUDE saat membuat indeks?

432

Saat belajar untuk ujian 70-433 saya perhatikan Anda dapat membuat indeks penutup dalam salah satu dari dua cara berikut.

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

-- ATAU --

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

Klausa INCLUDE baru bagi saya. Mengapa Anda menggunakannya dan pedoman apa yang akan Anda sarankan dalam menentukan apakah akan membuat indeks penutup dengan atau tanpa klausa TERMASUK?

Cory
sumber

Jawaban:

364

Jika kolom tidak ada di WHERE/JOIN/GROUP BY/ORDER BY, tetapi hanya di daftar kolom di SELECTklausa.

The INCLUDEklausul menambahkan data pada tingkat terendah / daun, bukan di pohon indeks. Ini membuat indeks lebih kecil karena itu bukan bagian dari pohon

INCLUDE columnsbukan kolom kunci dalam indeks, sehingga tidak dipesan. Ini berarti itu tidak benar-benar berguna untuk predikat, pengurutan dll seperti yang saya sebutkan di atas. Namun, mungkin berguna jika Anda memiliki sisa pencarian di beberapa baris dari kolom kunci

Artikel MSDN lain dengan contoh yang berfungsi

gbn
sumber
7
Jadi, ini akan menjadi teknik untuk membuat versi indeks tertutup yang lebih murah?
JMarsch
3
@ gbn, maukah Anda menjelaskan kalimat ini secara lebih rinci, dan jelaskan mengapa ini berarti bahwa klausa penyertaan tidak berguna untuk penyortiran, dll: "Klausa TERMASUK menambahkan data pada level terendah / daun, daripada di pohon indeks Ini membuat indeks lebih kecil karena itu bukan bagian dari pohon "
Tola Odejayi
4
@JMarsch: maaf atas jawaban yang terlambat, tapi ya, inilah tepatnya.
gbn
10
@Tola Odejayi: TERMASUK kolom bukan kolom kunci dalam indeks, sehingga tidak dipesan. Ini membuat mereka biasanya tidak berguna untuk BERGABUNG atau menyortir. Dan karena mereka bukan kolom kunci, mereka tidak duduk di seluruh struktur B-tree seperti kolom kunci
gbn
4
Meskipun ini adalah jawaban yang paling diterima, saya pikir diperlukan penjelasan lebih lanjut, bagaimana jika untuk beberapa pertanyaan kolom adalah bagian dari SELECTdan untuk sebagian tidak? \
Chisko
215

Anda akan menggunakan INCLUDE untuk menambahkan satu atau lebih kolom ke tingkat daun dari indeks yang tidak berkerumun, jika dengan melakukannya, Anda dapat "menutupi" kueri Anda.

Bayangkan Anda perlu menanyakan ID karyawan, ID departemen, dan nama belakang.

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

Jika Anda memiliki indeks non-clustered pada (EmployeeID, DepartmentID), setelah Anda menemukan karyawan untuk departemen yang diberikan, Anda sekarang harus melakukan "pencarian bookmark" untuk mendapatkan catatan penuh karyawan yang sebenarnya, hanya untuk mendapatkan kolom nama belakang . Itu bisa menjadi sangat mahal dalam hal kinerja, jika Anda menemukan banyak karyawan.

Jika Anda memasukkan nama belakang itu dalam indeks Anda:

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

maka semua informasi yang Anda butuhkan tersedia di tingkat daun dari indeks non-clustered. Hanya dengan mencari di indeks non-cluster dan menemukan karyawan Anda untuk departemen tertentu, Anda memiliki semua informasi yang diperlukan, dan pencarian bookmark untuk setiap karyawan yang ditemukan dalam indeks tidak lagi diperlukan -> Anda menghemat banyak waktu.

Jelas, Anda tidak dapat memasukkan setiap kolom dalam setiap indeks yang tidak berkerumun - tetapi jika Anda memang memiliki kueri yang hanya kehilangan satu atau dua kolom untuk "dicakup" (dan yang sering digunakan), akan sangat membantu TERMASUK bagi mereka menjadi indeks non-cluster yang cocok.

marc_s
sumber
25
Apakah Anda yakin akan menggunakan indeks ini? Mengapa EmployeeID? Anda hanya perlu DepartmentID di kolom kunci? Anda telah dikutip di sini sebagai yang asli: stackoverflow.com/q/6187904/27535
gbn
3
Penjelasan Anda baik tetapi sebenarnya tidak sesuai dengan use case yang Anda garis besarkan. Kolom kunci harus di filter atau JOINkunci dalam kueri, dan INCLUDEs perlu data yang Anda ambil tetapi tidak diurutkan.
JNK
15
Pertama-tama indeks Karyawan (EmployeeID, DepartmentID) tidak akan digunakan untuk memfilter DepartmentID = 5. Karena pesanannya tidak cocok
AnandPhadke
29

Diskusi ini kehilangan poin penting: Pertanyaannya bukan apakah "kolom-bukan-kunci" lebih baik dimasukkan sebagai indeks- kolom atau sebagai -kolom yang disertakan .

Pertanyaannya adalah seberapa mahal menggunakan mekanisme sertakan untuk memasukkan kolom yang tidak benar-benar diperlukan dalam indeks ? (biasanya bukan bagian dari mana-klausa, tetapi sering termasuk dalam pemilihan). Jadi dilema Anda selalu:

  1. Gunakan indeks pada id1, id2 ... idN sendiri atau
  2. Gunakan indeks pada id1, id2 ... idN plus sertakan col1, col2 ... colN

Di mana: id1, id2 ... idN adalah kolom yang sering digunakan dalam pembatasan dan col1, col2 ... colN adalah kolom yang sering dipilih, tetapi biasanya tidak digunakan dalam pembatasan

(Pilihan untuk memasukkan semua kolom ini sebagai bagian dari kunci-indeks selalu konyol (kecuali jika mereka juga digunakan dalam pembatasan) - karena akan selalu lebih mahal untuk mempertahankannya karena indeks harus diperbarui dan disortir bahkan ketika "kunci" tidak berubah).

Jadi gunakan opsi 1 atau 2?

Jawaban: Jika tabel Anda jarang diperbarui - sebagian besar dimasukkan ke / dihapus dari - maka relatif murah untuk menggunakan mekanisme-sertakan untuk memasukkan beberapa "kolom panas" (yang sering digunakan dalam pemilihan - tetapi tidak sering digunakan pada pembatasan) karena sisipan / penghapusan memerlukan indeks untuk diperbarui / disortir dan dengan demikian sedikit overhead tambahan terkait dengan menyimpan beberapa kolom tambahan saat sudah memperbarui indeks. Overhead adalah memori tambahan dan CPU yang digunakan untuk menyimpan info yang berlebihan pada indeks.

Jika kolom yang Anda pertimbangkan untuk ditambahkan sebagai kolom disertakan sering diperbarui (tanpa kunci indeks - kolom diperbarui) - atau - jika begitu banyak sehingga indeks menjadi dekat dengan salinan tabel Anda - gunakan opsi 1 Saya sarankan! Juga jika menambahkan kolom-sertakan tertentu ternyata tidak membuat perbedaan kinerja - Anda mungkin ingin melompati gagasan untuk menambahkannya :) Verifikasi bahwa mereka berguna!

Jumlah rata-rata baris per nilai yang sama dalam kunci (id1, id2 ... idN) dapat menjadi sangat penting.

Perhatikan bahwa jika kolom - yang ditambahkan sebagai kolom-indeks yang disertakan - digunakan dalam batasan : Selama indeks dapat digunakan (berdasarkan pembatasan terhadap indeks- kunci- kolom) - maka SQL Server cocok pembatasan kolom terhadap indeks (leaf-node-values) alih-alih menggunakan cara yang mahal di sekitar tabel itu sendiri.

Fredrik Solhaug
sumber
18

Kolom indeks dasar diurutkan, tetapi kolom yang disertakan tidak diurutkan. Ini menghemat sumber daya dalam mempertahankan indeks, sambil tetap memungkinkan untuk memberikan data di kolom yang disertakan untuk mencakup permintaan. Jadi, jika Anda ingin mencakup kueri, Anda dapat menempatkan kriteria pencarian untuk menemukan baris ke kolom yang diurutkan dari indeks, tetapi kemudian "memasukkan" kolom tambahan yang tidak disortir dengan data non-pencarian. Ini pasti membantu mengurangi jumlah penyortiran dan fragmentasi dalam pemeliharaan indeks.

onupdatecascade
sumber
7

Alasan mengapa (termasuk data di tingkat daun indeks) telah dijelaskan dengan baik. Alasan Anda memberikan dua getar tentang ini, adalah bahwa ketika Anda menjalankan kueri Anda, jika Anda tidak memiliki kolom tambahan termasuk (fitur baru di SQL 2005) SQL Server harus pergi ke indeks berkerumun untuk mendapatkan kolom tambahan yang membutuhkan lebih banyak waktu, dan menambahkan lebih banyak memuat ke layanan SQL Server, disk, dan memori (buffer cache untuk lebih spesifik) karena halaman data baru dimuat ke dalam memori, berpotensi mendorong data lain yang lebih sering dibutuhkan keluar dari cache buffer.

mrdenny
sumber
apakah ada cara untuk membuktikan bahwa itu sebenarnya menggunakan lebih sedikit memori? itu yang saya harapkan juga tetapi saya mendapatkan beberapa statis tentang ini di tempat kerja
Asken
Mengingat bahwa Anda harus memuat halaman dari tumpukan atau indeks berkerumun ke dalam memori serta halaman indeks yang berarti bahwa Anda memasukkan data duplikat ke dalam memori matematika menjadi sangat sederhana. Adapun cara untuk mengukurnya secara spesifik, tidak ada.
mrdenny
5

Pertimbangan tambahan yang belum saya lihat dalam jawaban yang sudah diberikan, adalah kolom yang disertakan dapat berupa tipe data yang tidak diizinkan sebagai kolom kunci indeks, seperti varchar (maks).

Ini memungkinkan Anda untuk memasukkan kolom seperti itu dalam indeks penutup. Saya baru-baru ini harus melakukan ini untuk memberikan permintaan yang dihasilkan nHibernate, yang memiliki banyak kolom di SELECT, dengan indeks yang berguna.

Robin Hames
sumber
3

Salah satu alasan untuk lebih memilih INCLUDEdaripada kolom-kunci jika Anda tidak membutuhkan kolom dalam kunci adalah dokumentasi. Itu membuat indeks yang berkembang jauh lebih mudah di masa depan.

Mempertimbangkan contoh Anda:

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

Indeks itu yang terbaik jika kueri Anda terlihat seperti ini:

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

Tentu saja Anda tidak boleh memasukkan kolom INCLUDEjika Anda bisa mendapatkan manfaat tambahan dari menempatkannya di bagian kunci. Kedua pertanyaan berikut sebenarnya akan lebih suka col2kolom di kunci indeks.

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

Mari kita asumsikan ini tidak terjadi dan kita miliki col2dalam INCLUDEklausa karena tidak ada untungnya memiliki di bagian pohon indeks.

Maju cepat beberapa tahun.

Anda perlu menyetel kueri ini:

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

Untuk mengoptimalkan kueri itu, indeks berikut akan bagus:

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

Jika Anda memeriksa indeks apa yang sudah Anda miliki di tabel itu, indeks Anda sebelumnya mungkin masih ada:

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

Sekarang Anda tahu itu Col2dan Col3bukan bagian dari pohon indeks dan karenanya tidak digunakan untuk mempersempit rentang indeks baca atau untuk memesan baris. Is agak aman untuk ditambahkan another_columnke akhir bagian kunci indeks (setelah col1). Ada sedikit risiko untuk memecahkan apa pun:

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

Indeks itu akan menjadi lebih besar, yang masih memiliki beberapa risiko, tetapi umumnya lebih baik memperpanjang indeks yang sudah ada dibandingkan dengan memperkenalkan yang baru.

Jika Anda akan memiliki indeks tanpa INCLUDE, Anda tidak bisa tahu permintaan apa yang akan Anda pecahkan dengan menambahkan another_colsetelahnya Col1.

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

Apa yang terjadi jika Anda menambahkan another_colantara Col1dan Col2? Apakah pertanyaan lain akan terganggu?

Ada "manfaat" lain dari INCLUDEkolom kunci vs. jika Anda menambahkan kolom itu hanya untuk menghindari mengambilnya dari tabel . Namun, saya menganggap aspek dokumentasi yang paling penting.

Untuk menjawab pertanyaan Anda:

pedoman apa yang akan Anda sarankan dalam menentukan apakah akan membuat indeks penutup dengan atau tanpa klausa INCLUDE?

Jika Anda menambahkan kolom ke indeks untuk tujuan tunggal agar kolom itu tersedia dalam indeks tanpa mengunjungi tabel, masukkan ke dalam INCLUDEklausa.

Jika menambahkan kolom ke kunci indeks membawa manfaat tambahan (misalnya untuk order byatau karena itu dapat mempersempit rentang indeks baca) menambahkannya ke kunci.

Anda dapat membaca diskusi yang lebih panjang tentang ini di sini:

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

Markus Winand
sumber
2

Ada batas ukuran total semua kolom yang diuraikan dalam definisi indeks. Meskipun begitu, saya tidak pernah harus membuat indeks seluas itu. Bagi saya, keuntungan yang lebih besar adalah fakta bahwa Anda dapat mencakup lebih banyak permintaan dengan satu indeks yang menyertakan kolom karena tidak harus didefinisikan dalam urutan tertentu. Pikirkan adalah sebagai indeks dalam indeks. Salah satu contoh adalah StoreID (di mana StoreID adalah selektivitas rendah yang berarti bahwa setiap toko dikaitkan dengan banyak pelanggan) dan kemudian data demografi pelanggan (LastName, FirstName, DOB): Jika Anda hanya menyejajarkan kolom-kolom dalam urutan ini (StoreID, LastName , FirstName, DOB), Anda hanya dapat mencari pelanggan yang Anda tahu StoreID dan LastName secara efisien.

Di sisi lain, mendefinisikan indeks pada StoreID dan termasuk LastName, FirstName, kolom DOB akan membiarkan Anda pada dasarnya melakukan dua pencarian indeks pada StoreID dan kemudian mencari predikat pada salah satu kolom yang disertakan. Ini akan memungkinkan Anda menutupi semua permutasi pencarian yang mungkin selama itu dimulai dengan StoreID.

mEmENT0m0RI
sumber