Saya punya pertanyaan di mana menggunakan select *
tidak hanya membaca jauh lebih sedikit, tetapi juga menggunakan waktu CPU secara signifikan lebih sedikit daripada menggunakan select c.Foo
.
Ini pertanyaannya:
select top 1000 c.ID
from ATable a
join BTable b on b.OrderKey = a.OrderKey and b.ClientId = a.ClientId
join CTable c on c.OrderId = b.OrderId and c.ShipKey = a.ShipKey
where (a.NextAnalysisDate is null or a.NextAnalysisDate < @dateCutOff)
and b.IsVoided = 0
and c.ComplianceStatus in (3, 5)
and c.ShipmentStatus in (1, 5, 6)
order by a.LastAnalyzedDate
Ini selesai dengan 2.473.658 pembacaan logis, sebagian besar dalam Tabel B. Ini menggunakan 26.562 CPU dan memiliki durasi 7.965.
Ini adalah paket permintaan yang dihasilkan:
Pada PasteThePlan: https://www.brentozar.com/pastetheplan/?id=BJAp2mQIQ
Ketika saya beralih c.ID
ke *
, kueri selesai dengan 107.049 pembacaan logis, cukup merata di antara ketiga tabel. Ini menggunakan 4.266 CPU dan memiliki durasi 1.147.
Ini adalah paket permintaan yang dihasilkan:
Pada PasteThePlan: https://www.brentozar.com/pastetheplan/?id=SyZYn7QUQ
Saya mencoba menggunakan petunjuk kueri yang disarankan oleh Joe Obbish, dengan hasil ini:
select c.ID
tanpa petunjuk: https://www.brentozar.com/pastetheplan/?id=SJfBdOELm
select c.ID
dengan petunjuk: https://www.brentozar.com/pastetheplan/ ? id = B1W ___ N87
select *
tanpa petunjuk: https://www.brentozar.com/pastetheplan/?id=HJ6qddEIm
select *
dengan petunjuk: https://www.brentozar.com/pastetheplan/?id=rJhhudNIQ
Menggunakan OPTION(LOOP JOIN)
petunjuk dengan select c.ID
secara drastis mengurangi jumlah bacaan dibandingkan dengan versi tanpa petunjuk, tetapi masih melakukan sekitar 4x jumlah bacaan select *
kueri tanpa petunjuk apa pun. Menambahkan OPTION(RECOMPILE, HASH JOIN)
ke select *
kueri membuatnya berkinerja lebih buruk daripada hal lain yang telah saya coba.
Setelah memperbarui statistik pada tabel dan indeksnya menggunakan WITH FULLSCAN
, select c.ID
kueri berjalan jauh lebih cepat:
select c.ID
sebelum pembaruan: https://www.brentozar.com/pastetheplan/?id=SkiYoOEUm
select *
sebelum memperbarui: https://www.brentozar.com/ pastetheplan /? id = ryrvodEUX
select c.ID
setelah pembaruan: https://www.brentozar.com/pastetheplan/?id=B1MRoO487
select *
setelah pembaruan: https://www.brentozar.com/pastetheplan/?id=Hk7si_V8m
select *
masih melebihi select c.ID
dari segi total durasi dan jumlah membaca ( select *
memiliki sekitar setengah dibaca) tetapi tidak digunakan lebih CPU. Secara keseluruhan mereka jauh lebih dekat daripada sebelum pembaruan, namun rencananya masih berbeda.
Perilaku yang sama terlihat pada 2016 berjalan dalam mode Kompatibilitas 2014 dan pada 2014. Apa yang bisa menjelaskan perbedaan antara kedua rencana? Mungkinkah indeks "benar" belum dibuat? Bisakah statistik sedikit ketinggalan zaman menyebabkan ini?
Saya mencoba memindahkan predikat ke ON
bagian gabungan, dengan berbagai cara, tetapi rencana kuerinya sama setiap kali.
Setelah Indeks Dibangun Kembali
Saya membangun kembali semua indeks pada tiga tabel yang terlibat dalam kueri. c.ID
masih melakukan paling banyak membaca (lebih dari dua kali lipat *
), tetapi penggunaan CPU adalah sekitar setengah dari *
versi. The c.ID
Versi juga tumpah ke tempdb pada pemilahan ATable
:
c.ID
: https://www.brentozar.com/pastetheplan/?id=HyHIeDO87
*
: https://www.brentozar.com/pastetheplan/?id=rJ4deDOIQ
Saya juga mencoba memaksanya beroperasi tanpa paralelisme, dan itu memberi saya pertanyaan dengan kinerja terbaik: https://www.brentozar.com/pastetheplan/?id=SJn9-vuLX
Saya perhatikan jumlah eksekusi operator SETELAH pencarian indeks besar yang melakukan pemesanan hanya dieksekusi 1.000 kali dalam versi single-threaded, tetapi melakukan lebih signifikan dalam versi Paralelized, antara 2.622 dan 4.315 eksekusi berbagai operator.
select c.ID
kueri lebih cepat, tetapi masih melakukan beberapa pekerjaan tambahan yang dilakukanselect *
kueri, tanpa petunjuk.Statistik basi tentu dapat menyebabkan pengoptimal untuk memilih metode yang buruk dalam menemukan data. Sudahkah Anda mencoba melakukan
UPDATE STATISTICS ... WITH FULLSCAN
atau melakukan penuhREBUILD
pada indeks? Coba itu dan lihat apakah itu membantu.MEMPERBARUI
Menurut pembaruan dari OP:
Jadi, sekarang, jika satu-satunya tindakan yang diambil adalah
UPDATE STATISTICS
, maka coba lakukan indeksREBUILD
(bukanREORGANIZE
) seperti yang saya lihat yang membantu dengan perkiraan jumlah baris di mana keduanyaUPDATE STATISTICS
dan indeksREORGANIZE
tidak.sumber
a) menulis ulang setiap meja sebagai subquery, berikut aturan-aturan ini:
b) SELECT - put bergabung kolom pertama
c) predikat - pindah ke subqueries masing-masing
d) ORDER BY - pindah ke mereka masing-masing subqueries, urutkan pada JOIN COLUMNS FIRST
e) Tambahkan kueri wrapper untuk pengurutan akhir Anda dan SELECT.
Idenya adalah untuk pre-sortir kolom bergabung di dalam setiap subselect, menempatkan kolom bergabung pertama di setiap daftar pilih.
Inilah yang saya maksud ....
sumber
ORDER BY
tidak valid dalam subquery tanpa TOP, FORXML, dll. Saya mencobanya tanpaORDER BY
klausa tetapi itu adalah rencana yang sama.