Saya perlu mengoptimalkan SELECT
pernyataan tetapi SQL Server selalu melakukan pemindaian indeks daripada mencari. Ini adalah permintaan yang, tentu saja, dalam prosedur tersimpan:
CREATE PROCEDURE dbo.something
@Status INT = NULL,
@IsUserGotAnActiveDirectoryUser BIT = NULL
AS
SELECT [IdNumber], [Code], [Status], [Sex],
[FirstName], [LastName], [Profession],
[BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE (@Status IS NULL OR [Status] = @Status)
AND
(
@IsUserGotAnActiveDirectoryUser IS NULL
OR
(
@IsUserGotAnActiveDirectoryUser IS NOT NULL AND
(
@IsUserGotAnActiveDirectoryUser = 1 AND ActiveDirectoryUser <> ''
)
OR
(
@IsUserGotAnActiveDirectoryUser = 0 AND ActiveDirectoryUser = ''
)
)
)
Dan ini adalah indeksnya:
CREATE INDEX not_relevent ON dbo.Employee
(
[Status] DESC,
[ActiveDirectoryUser] ASC
)
INCLUDE (...all the other columns in the table...);
Rencana:
Mengapa SQL Server memilih pemindaian? Bagaimana saya bisa memperbaikinya?
Definisi kolom:
[Status] int NOT NULL
[ActiveDirectoryUser] VARCHAR(50) NOT NULL
Parameter status dapat:
NULL: all status,
1: Status= 1 (Active employees)
2: Status = 2 (Inactive employees)
IsUserGotAnActiveDirectoryUser dapat:
NULL: All employees
0: ActiveDirectoryUser is empty for that employee
1: ActiveDirectoryUser got a valid value (not null and not empty)
sql-server
sql-server-2012
index
optimization
Bestter
sumber
sumber
@Status
?Status DESC
? Berapa banyak nilai yang adaStatus
, untuk apa mereka (jika jumlahnya kecil), dan apakah masing-masing nilai diwakili secara setara? Tunjukkan kami hasilSELECT TOP (20) [Status], c = COUNT(*) FROM dbo.Employee GROUP BY [Status] ORDER BY c DESC;
Jawaban:
Saya tidak berpikir pemindaian disebabkan oleh pencarian untuk string kosong (dan sementara Anda bisa menambahkan indeks yang difilter untuk kasus itu, itu hanya akan membantu variasi permintaan yang sangat spesifik). Anda lebih cenderung menjadi korban dari sniffing parameter dan satu paket tidak dioptimalkan untuk semua kombinasi berbagai parameter (dan nilai parameter) yang akan Anda berikan untuk kueri ini.
Saya menyebutnya prosedur "wastafel dapur" , karena Anda mengharapkan satu permintaan untuk menyediakan semua hal, termasuk wastafel dapur.
Saya punya video tentang solusi saya untuk ini di sini , tetapi pada dasarnya, pengalaman terbaik yang saya miliki untuk pertanyaan seperti itu adalah:
OPTION (RECOMPILE)
- ini mencegah nilai parameter spesifik dari memaksa jenis paket yang salah, terutama membantu ketika Anda memiliki kemiringan data, statistik yang buruk, atau ketika eksekusi pertama dari pernyataan menggunakan nilai atipikal yang akan mengarah pada rencana yang berbeda daripada nanti dan lebih sering eksekusi.optimize for ad hoc workloads
- ini mencegah variasi kueri yang hanya digunakan satu kali dari mencemari cache rencana Anda.Aktifkan optimisasi untuk beban kerja ad hoc:
Ubah prosedur Anda:
Setelah Anda memiliki beban kerja berdasarkan kumpulan pertanyaan yang dapat Anda monitor, Anda dapat menganalisis eksekusi dan melihat mana yang paling diuntungkan dari indeks tambahan atau berbeda - Anda dapat melakukan ini dari berbagai sudut, dari yang sederhana "yang merupakan kombinasi dari parameter disediakan paling sering? " ke "kueri individual manakah yang memiliki runtime terpanjang?" Kami tidak dapat menjawab pertanyaan-pertanyaan itu hanya berdasarkan kode Anda, kami hanya dapat menyarankan bahwa indeks apa pun hanya akan membantu untuk subset dari semua kombinasi parameter yang mungkin Anda coba dukung. Misalnya, jika
@Status
adalah NULL, maka tidak ada upaya untuk mencegah bahwa indeks non-cluster adalah mungkin. Jadi untuk kasus-kasus di mana pengguna tidak peduli tentang status, Anda akan mendapatkan pemindaian, kecuali jika Anda memiliki indeks yang sesuai dengan klausa lain (tetapi indeks tersebut tidak akan berguna juga, mengingat logika permintaan Anda saat ini) - baik string kosong atau tidak string kosong tidak sepenuhnya selektif).Dalam hal ini, tergantung pada set
Status
nilai yang mungkin dan seberapa terdistribusi nilai-nilai itu,OPTION (RECOMPILE)
mungkin tidak diperlukan. Tetapi jika Anda memiliki beberapa nilai yang akan menghasilkan 100 baris dan beberapa nilai yang akan menghasilkan ratusan ribu, Anda mungkin menginginkannya di sana (bahkan dengan biaya CPU, yang seharusnya marjinal mengingat kompleksitas kueri ini), sehingga Anda dapat dapatkan berusaha dalam banyak kasus sebanyak mungkin. Jika rentang nilai cukup terbatas, Anda bahkan bisa melakukan sesuatu yang rumit dengan SQL dinamis, di mana Anda berkata "Saya memiliki nilai yang sangat selektif ini@Status
, jadi ketika nilai tertentu dilewatkan, buat sedikit perubahan pada teks kueri sehingga ini dianggap sebagai kueri yang berbeda dan dioptimalkan untuk nilai param itu. "sumber
Penafian : Beberapa hal dalam jawaban ini dapat membuat DBA tersentak. Saya mendekatinya dari sudut pandang kinerja murni - cara mendapatkan Indeks Mencari ketika Anda selalu mendapatkan Indeks Scan.
Dengan hal itu, ini dia.
Permintaan Anda adalah apa yang dikenal sebagai "permintaan wastafel dapur" - satu permintaan yang dimaksudkan untuk memenuhi berbagai kondisi pencarian yang memungkinkan. Jika pengguna menetapkan
@status
ke nilai, Anda ingin memfilter pada status itu. Jika@status
adalahNULL
, mengembalikan semua status, dan sebagainya.Ini menimbulkan masalah dengan pengindeksan, tetapi mereka tidak terkait dengan sargability, karena semua kondisi pencarian Anda adalah kriteria "sama dengan".
Ini masuk akal:
Ini tidak masuk akal karena SQL Server perlu mengevaluasi
ISNULL([status], 0)
untuk setiap baris alih-alih mencari nilai tunggal dalam indeks:Saya telah menciptakan kembali masalah wastafel dapur dalam bentuk yang lebih sederhana:
Jika Anda mencoba yang berikut ini, Anda akan mendapatkan Pemindaian Indeks, meskipun A adalah kolom pertama dari indeks:
Ini, bagaimanapun, menghasilkan Indeks Mencari:
Selama Anda menggunakan jumlah parameter yang dapat dikelola (dua dalam kasus Anda), Anda mungkin bisa hanya
UNION
sekelompok permintaan pencarian - pada dasarnya semua permutasi kriteria pencarian. Jika Anda memiliki tiga kriteria, ini akan terlihat berantakan, dengan empat kriteria itu akan sepenuhnya tidak dapat dikelola. Anda sudah diperingatkan.Untuk yang ketiga dari keempat untuk menggunakan Indeks Mencari, Anda akan memerlukan indeks kedua
(B, A)
. Begini cara kueri Anda terlihat dengan perubahan-perubahan ini (termasuk refactoring saya atas kueri agar lebih mudah dibaca).... plus Anda akan memerlukan indeks tambahan
Employee
dengan dua kolom indeks dibalik.Untuk kelengkapan, saya harus menyebutkan bahwa
x=@x
secara implisit berarti itux
tidak mungkinNULL
karenaNULL
tidak pernah sama denganNULL
. Itu sedikit menyederhanakan kueri.Dan, ya, jawaban SQL dinamis Aaron Bertrand adalah pilihan yang lebih baik dalam kebanyakan kasus (yaitu kapan pun Anda dapat hidup dengan kompilasi).
sumber
Pertanyaan dasar Anda tampaknya adalah "Mengapa" dan saya pikir Anda mungkin menemukan jawabannya sekitar menit 55 atau lebih dari presentasi Hebat ini oleh Adam Machanic di TechEd beberapa tahun yang lalu.
Saya menyebutkan 5 menit pada menit 55 tetapi seluruh presentasi sepadan dengan waktu. Jika Anda melihat rencana kueri untuk kueri Anda, saya yakin Anda akan menemukannya memiliki Predikat Sisa untuk pencarian. Pada dasarnya SQL tidak dapat "melihat" semua bagian dari indeks karena beberapa dari mereka disembunyikan oleh ketidaksetaraan dan kondisi lainnya. Hasilnya adalah pemindaian indeks untuk super set berdasarkan Predikat. Hasil itu spooled dan kemudian dipindai kembali menggunakan predikat residual.
Periksa properti Operator Pindai (F4) dan lihat apakah Anda memiliki "Cari Predikat" dan "Predikat" di daftar properti.
Seperti yang telah ditunjukkan orang lain, kueri sulit untuk diindeks apa adanya. Saya telah mengerjakan banyak yang serupa baru-baru ini dan masing-masing membutuhkan solusi yang berbeda. :(
sumber
Sebelum kita mempertanyakan apakah pencarian indeks lebih disukai daripada pemindaian indeks, satu aturan praktis adalah untuk memeriksa berapa banyak baris yang dikembalikan vs total baris tabel yang mendasarinya. Misalnya, jika Anda mengharapkan permintaan Anda mengembalikan 10 baris dari 1 juta baris, maka pencarian indeks mungkin sangat disukai daripada pemindaian indeks. Namun, jika beberapa ribu baris (atau lebih) harus dikembalikan dari kueri, maka pencarian indeks TIDAK mungkin lebih disukai.
Permintaan Anda tidak rumit, jadi jika Anda dapat memposting rencana eksekusi, kami mungkin memiliki ide yang lebih baik untuk membantu Anda.
sumber
ini hanya yang asli yang diformat
ini revisi - tidak 100% yakin tentang hal itu tetapi (mungkin) mencobanya
walaupun satu ATAU mungkin akan menjadi masalah,
ini akan memecah pada ActiveDirectoryUser null
sumber