Saya perhatikan bahwa ketika ada tumpahan ke acara tempdb (menyebabkan kueri lambat) yang sering kali perkiraan baris jauh dari gabungan tertentu. Saya telah melihat peristiwa tumpahan terjadi dengan menggabungkan dan hash bergabung dan mereka sering meningkatkan runtime 3x menjadi 10x. Pertanyaan ini menyangkut bagaimana meningkatkan perkiraan baris dengan asumsi bahwa hal itu akan mengurangi kemungkinan peristiwa tumpahan.
Jumlah baris sebenarnya 40k.
Untuk kueri ini, paket menampilkan taksiran baris buruk (11,3 baris):
select Value
from Oav.ValueArray
where ObjectId = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Untuk kueri ini, paket menampilkan estimasi baris yang bagus (baris 56rb):
declare @a bigint = (select convert(bigint, Value) NodeId
from Oav.ValueArray
where PropertyId = 3331
and ObjectId = 3540233
and Sequence = 2);
select Value
from Oav.ValueArray
where ObjectId = @a
and PropertyId = 2840
option (recompile);
Dapatkah statistik atau petunjuk ditambahkan untuk meningkatkan perkiraan baris untuk kasus pertama? Saya mencoba menambahkan statistik dengan nilai filter tertentu (properti = 2840) tetapi tidak bisa mendapatkan kombinasi yang benar atau mungkin diabaikan karena ObjectId tidak diketahui pada waktu kompilasi dan mungkin memilih rata-rata di atas semua ObjectIds.
Apakah ada mode di mana ia akan melakukan penyelidikan terlebih dahulu dan kemudian menggunakannya untuk menentukan estimasi baris atau haruskah terbang secara membabi buta?
Properti khusus ini memiliki banyak nilai (40k) pada beberapa objek dan nol pada sebagian besar. Saya akan senang dengan petunjuk di mana jumlah baris maksimum yang diharapkan untuk suatu join dapat ditentukan. Ini adalah masalah yang umumnya menghantui karena beberapa parameter dapat ditentukan secara dinamis sebagai bagian dari gabungan atau akan lebih baik ditempatkan dalam tampilan (tidak ada dukungan untuk variabel).
Apakah ada parameter yang dapat disesuaikan untuk meminimalkan kemungkinan tumpahan ke tempdb (mis. Memori min per kueri)? Rencana yang kuat tidak berpengaruh pada estimasi.
Edit 2013.11.06 : Tanggapan terhadap komentar dan informasi tambahan:
Berikut adalah gambar rencana kueri. Peringatannya adalah tentang kardinalitas / mencari predikat dengan orang yang dipertobatkan ():
Per komentar @Aaron Bertrand, saya mencoba mengganti convert () sebagai tes:
create table Oav.SeekObject (
LookupId bigint not null primary key,
ObjectId bigint not null
);
insert into Oav.SeekObject (
LookupId, ObjectId
) VALUES (
1, 3540233
)
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.SeekObject
where LookupId = 1)
and PropertyId = 2840
option (recompile);
Sebagai tempat tujuan yang aneh namun berhasil, juga memungkinkannya untuk menyingkat pencarian:
select Value
from Oav.ValueArray
where ObjectId = (select ObjectId
from Oav.ValueArray
where PropertyId = 2840
and ObjectId = 3540233
and Sequence = 2)
and PropertyId = 2840
option (recompile);
Kedua daftar ini pencarian kunci yang tepat tetapi hanya yang pertama daftar "Output" dari ObjectId. Saya kira itu menunjukkan yang kedua memang korsleting?
Dapatkah seseorang memverifikasi apakah penyelidikan baris tunggal pernah dilakukan untuk membantu dengan perkiraan baris? Tampaknya salah untuk membatasi pengoptimalan hanya perkiraan histogram ketika pencarian PK baris tunggal dapat sangat meningkatkan akurasi pencarian ke dalam histogram (terutama jika ada potensi tumpahan atau riwayat). Ketika ada 10 sub-gabungan ini dalam kueri nyata, idealnya mereka akan terjadi secara paralel.
Catatan tambahan, karena sql_variant menyimpan tipe dasarnya (SQL_VARIANT_PROPERTY = BaseType) di dalam isian itu sendiri, saya akan mengharapkan konversi () hampir tanpa biaya asalkan konversi "langsung" (mis. Bukan string ke desimal tetapi bukan int ke int atau mungkin int ke bigint). Karena itu tidak diketahui pada waktu kompilasi tetapi dapat diketahui oleh pengguna, mungkin fungsi "AssumeType (type, ...)" untuk sql_variants akan memungkinkan mereka diperlakukan lebih transparan.
sumber
declare @a bigint =
seperti yang Anda lakukan tampaknya merupakan solusi alami bagi saya, mengapa itu tidak dapat diterima?CONVERT()
kolom dan kemudian bergabung dengan mereka. Ini tentu tidak efisien dalam banyak kasus. Dalam yang khusus ini, hanya satu nilai yang akan dikonversi jadi itu mungkin bukan masalah tetapi indeks apa yang Anda miliki di atas meja? Desain EAV biasanya berkinerja baik, hanya dengan pengindeksan yang tepat (yang berarti banyak indeks dalam tabel yang biasanya sempit).Jawaban:
Saya tidak akan berkomentar tentang tumpahan, tempdb, atau petunjuk karena permintaan tampaknya cukup sederhana untuk memerlukan banyak pertimbangan. Saya pikir pengoptimal SQL-Server akan melakukan tugasnya dengan cukup baik, jika ada indeks yang cocok untuk kueri.
Dan pemisahan Anda menjadi dua pertanyaan bagus karena menunjukkan indeks apa yang akan berguna. Bagian pertama:
membutuhkan indeks pada
(PropertyId, ObjectId, Sequence)
termasukValue
. Saya akan membuatnyaUNIQUE
aman. Kueri akan memunculkan kesalahan selama runtime jika lebih dari satu baris dikembalikan, jadi ada baiknya memastikan terlebih dahulu bahwa ini tidak akan terjadi, dengan indeks unik:Bagian kedua dari permintaan:
membutuhkan indeks untuk
(PropertyId, ObjectId)
menyertakanValue
:Jika efisiensi tidak ditingkatkan atau indeks ini tidak digunakan atau masih ada perbedaan dalam perkiraan baris yang muncul, maka akan perlu untuk melihat lebih jauh ke dalam permintaan ini.
Dalam hal ini, konversi (diperlukan dari desain EAV dan penyimpanan tipe data yang berbeda di kolom yang sama) adalah kemungkinan penyebab dan solusi pemecahan Anda (seperti @AAron Bertrand dan @Paul White komentar) permintaan menjadi dua bagian tampak alami dan cara untuk pergi. Desain ulang sehingga memiliki tipe data yang berbeda di kolomnya masing-masing mungkin berbeda.
sumber
Sebagai jawaban parsial untuk pertanyaan eksplisit tentang peningkatan statistik ...
Perhatikan bahwa estimasi baris bahkan untuk kasus yang dipisah secara terpisah masih mati sebesar 10X (4k vs yang diharapkan 40k).
Histogram statistik kemungkinan menyebar terlalu tipis untuk properti itu karena tabel baris panjang (vertikal), 3,5M, dan properti khusus itu sangat jarang.
Buat statistik tambahan (agak berlebihan dengan statistik IX) untuk properti jarang:
Yang asli:
Dengan konversi () dihapus (layak):
Dengan konversi () dihapus (ciruit pendek):
Masih mati ~ 2X kemungkinan karena> 99,9% dari objek tidak memiliki Properti 2840 yang didefinisikan pada mereka sama sekali. Bahkan hanya untuk kasus uji ini properti ada hanya pada 1 dari 200k Objek berbeda dari tabel baris 3.5M. Sungguh menakjubkan, itu bisa sedekat itu. Menyesuaikan filter menjadi ObjectIds lebih sedikit,
Hmm, tidak ada perubahan ... Didukung bahwa ditambahkan "dengan pemindaian penuh" ke akhir statistik (mungkin mengapa dua sebelumnya tidak bekerja) dan ya:
Yay. Jadi dalam tabel yang sangat vertikal dengan IX yang luas, menambahkan statistik yang difilter tambahan tampaknya merupakan peningkatan besar (terutama untuk kombinasi kunci yang jarang tetapi sangat bervariasi).
sumber