Rencana Eksekusi BUKAN menggunakan INDEX, Ini menggunakan Table Scan

9

Saya tahu ketika harus menggunakan indeks atau scan tabel, SQL Server menggunakan statistik untuk melihat mana yang lebih baik.

Saya punya meja dengan 20 juta baris. Saya memiliki indeks pada (SnapshotKey, Measure) dan permintaan ini:

select Measure, SnapshotKey, MeasureBand
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

Kueri mengembalikan 500r baris. Jadi kueri hanya memilih 2,5% dari baris tabel.

Pertanyaannya adalah mengapa SQL Server tidak menggunakan indeks nonclustered yang saya miliki, dan menggunakan pemindaian tabel sebagai gantinya?

Statistik diperbarui.

Baik untuk menyebutkan bahwa kinerja permintaan itu bagus.

Pemindaian Tabel

Pemindaian Tabel

Indeks Paksa

Indeks Kekuatan

Tabel / Struktur Indeks

CREATE TABLE [t1](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [date] NOT NULL,
    [Measure] [nvarchar](30) NOT NULL,
    [MeasureBand] [nvarchar](30) NOT NULL,
    -- and many more fields
) ON [PRIMARY]

Tidak ada PK di atas meja, karena ini adalah gudang data.

CREATE NONCLUSTERED INDEX [nci_SnapshotKeyMeasure] ON [t1]
(
    [SnapshotKey] ASC,
    [Measure] ASC
)

sumber

Jawaban:

16

Pencarian indeks mungkin bukan pilihan terbaik jika Anda mengembalikan banyak baris dan / atau baris sangat lebar. Pencarian bisa mahal jika indeks Anda tidak mencakup. Lihat # 2 di sini .

Dalam skenario Anda, pengoptimal kueri memperkirakan bahwa melakukan 50.000 pencarian individu akan lebih mahal daripada pemindaian tunggal. Pilihan pengoptimal antara pemindaian dan pencarian (dengan pencarian RID untuk kolom yang dibutuhkan oleh kueri, tetapi tidak ada dalam indeks nonclustered) didasarkan pada perkiraan biaya setiap alternatif.

Pengoptimal selalu memilih alternatif biaya terendah yang dipertimbangkannya. Jika Anda melihat properti Perkiraan Biaya Subtree di simpul akar dari dua rencana eksekusi, Anda akan melihat bahwa rencana pemindaian memiliki perkiraan biaya yang lebih rendah daripada rencana pencarian. Akibatnya, pengoptimal memilih pemindaian. Itu pada dasarnya adalah jawaban untuk pertanyaan Anda.

Sekarang, model biaya yang digunakan oleh pengoptimal didasarkan pada asumsi dan "angka ajaib" yang sangat tidak sesuai dengan karakteristik kinerja sistem Anda. Secara khusus, satu asumsi yang dibuat dalam model adalah bahwa kueri mulai mengeksekusi dengan tidak ada data atau halaman indeks yang diperlukan sudah ada dalam memori. Lain adalah bahwa I / O berurutan (diharapkan untuk pemindaian) lebih murah daripada pola I / O acak yang diasumsikan untuk Pencarian RID. Ada banyak asumsi dan peringatan semacam itu, terlalu banyak untuk dibahas secara rinci di sini.

Namun demikian, model biaya secara keseluruhan telah terbukti menghasilkan rencana yang umumnya "cukup baik" untuk sebagian besar permintaan, pada sebagian besar skema basis data, pada sebagian besar konfigurasi perangkat keras, sebagian besar waktu, di mana-mana. Itu pencapaian yang luar biasa, jika Anda pikirkan.

Keterbatasan model dan faktor-faktor lain kadang-kadang berarti pengoptimal memilih rencana yang, pada kenyataannya, tidak "cukup baik" sama sekali. Anda melaporkan bahwa "kinerja itu baik", sehingga sepertinya tidak demikian di sini.

Aaron Bertrand
sumber
9

Anda sebenarnya memiliki 595.947 baris yang cocok, yaitu sekitar 3% dari data Anda. Jadi biaya pencarian bertambah dengan cepat. Misalkan Anda memiliki 100 baris per halaman di tabel Anda, itu 200.000 halaman untuk dibaca dalam pemindaian tabel. Itu jauh lebih murah daripada melakukan 595.947 pencarian.

Dengan GROUP BYklausa dalam pertanyaan, saya pikir Anda akan lebih baik dengan kunci komposit aktif (Measure, SnapshotKey, MeasureBand).

Lihatlah saran "indeks hilang". Ini memberitahu Anda untuk memasukkan kolom untuk menghindari pencarian. Lebih umum, jika Anda mereferensikan kolom lain dalam kueri Anda, mereka harus berada di kunci atau INCLUDEklausa indeks baru. Kalau tidak, masih perlu melakukan 595.947 pencarian untuk mendapatkan nilai-nilai itu.

Misalnya, untuk kueri:

select Measure, SnapshotKey, MeasureBand, SUM(NumLoans), SUM(PrinBal)
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

... Anda akan membutuhkan:

CREATE INDEX ixWhatever 
ON t1 (Measure, SnapshotKey, MeasureBand) 
INCLUDE (NumLoans,PrinBal);
Rob Farley
sumber
6
  1. Bidang dalam kondisi WHERE Anda bukan bidang utama indeks.

  2. Anda telah measuredidefinisikan sebagai NVARCHAR sehingga awalan literal dengan N: where Measure = N'FinanceFICOScore'.

Pertimbangkan untuk membuat Indeks Berkelompok pada SnapshotKey. Jika unik maka bisa menjadi PK (dan Clustered). Jika tidak unik maka itu tidak bisa menjadi PK, tetapi masih bisa menjadi Indeks Clustered yang tidak unik. Maka indeks Anda yang tidak berkerumun hanya pada measurekolom.

Dan, mengingat bahwa bidang pertama di GROUP BYjuga measure, itu juga akan mendapat manfaat dari measuremenjadi bidang terkemuka.

Bahkan, untuk operasi ini, Anda mungkin perlu mendefinisikan NonClustered Index Measure, SnapshotKey, MeasureBand, sesuai urutan yang cocok dengan GROUP BYklausa. Ukuran bijaksana yang hanya benar-benar menambahkan MeasureBandkarena indeks NonClustered sudah didasarkan pada Measure, dan MeasureKeysudah termasuk dalam indeks karena sekarang adalah kunci Indeks Clustered (tidak, Measuretidak akan diduplikasi dalam indeks NonClustered).

@Rob telah menyebutkan dalam komentar yang sekarang dihapus pada jawabannya bahwa menyelesaikan masalah ini hanya mensyaratkan bahwa Indeks NonClustered didefinisikan dengan tiga bidang ini dalam urutan ini, dan bahwa membuat Indeks Clustered (non-unik) SnapshotKeytidak diperlukan . Walaupun dia mungkin benar (saya berharap lebih sedikit bidang akan bekerja), saya masih berpendapat bahwa memiliki Indeks Clustered bermanfaat untuk tidak hanya operasi ini, tetapi mungkin sebagian besar lainnya.

Solomon Rutzky
sumber
Diskusi tentang jawaban ini telah dipindahkan ke obrolan .
Paul White 9