Jangan salah paham – Saya suka properti Baca Baris Aktual yang kami lihat tiba di rencana eksekusi SQL Server pada akhir tahun 2015. Namun di SQL Server 2016 SP1, kurang dari dua bulan yang lalu (dan mengingat kami memiliki Natal di antaranya, saya tidak berpikir banyak waktu sejak itu diperhitungkan), kami mendapat tambahan menarik lainnya – Perkiraan Jumlah Baris yang Akan Dibaca (oh, dan ini agak tergantung pada item Connect yang saya kirimkan, keduanya menunjukkan bahwa Connect Items layak dikirim dan membuat posting ini memenuhi syarat untuk T-SQL Selasa bulan ini, dipandu oleh Brent Ozar (@brento) dengan topik item Connect ).
Mari kita rekap sejenak… ketika SQL Engine mengakses data dalam sebuah tabel, ia menggunakan operasi Scan atau operasi Seek. Dan kecuali Seek itu memiliki Predikat Seek yang dapat mengakses paling banyak satu baris (karena mencari kecocokan kesetaraan pada sekumpulan kolom – bisa jadi hanya satu kolom – yang diketahui unik), maka Seek akan melakukan RangeScan, dan berperilaku seperti Scan, tepat di seberang subset baris yang dipenuhi oleh Seek Predicate.
Baris yang dipenuhi oleh Seek Predicate (dalam kasus operasi Seek RangeScan) atau semua baris dalam tabel (dalam kasus operasi Scan) pada dasarnya diperlakukan dengan cara yang sama. Keduanya mungkin dihentikan lebih awal jika tidak ada lagi baris yang diminta dari operator di sebelah kirinya, misalnya jika operator Top di suatu tempat telah mengambil cukup banyak baris, atau jika Operator Penggabungan tidak memiliki baris lagi untuk dicocokkan. Dan keduanya mungkin disaring lebih lanjut oleh Predikat Residual (ditampilkan sebagai properti 'Predikat') bahkan sebelum baris dilayani oleh operator Scan/Seek. Properti "Jumlah Baris" dan "Perkiraan Jumlah Baris" akan memberi tahu kami berapa banyak baris yang diharapkan akan diproduksi oleh operator, tetapi kami tidak memiliki informasi apa pun tentang bagaimana baris akan difilter hanya dengan Predikat Seek. Kita bisa melihat TableCardinality, tapi ini hanya berguna untuk operator Scan, di mana ada kemungkinan Scan dapat melihat seluruh tabel untuk baris yang dibutuhkan. Itu sama sekali tidak berguna untuk Seeks.
Kueri yang saya jalankan di sini bertentangan dengan database WideWorldImporters, dan adalah:
PILIH JUMLAH(*)FROM Penjualan.PesananWHERE SalespersonPersonID =7AND YEAR(OrderDate) =2013AND MONTH(OrderDate) =4;
Selanjutnya, saya memiliki indeks yang sedang dimainkan:
BUAT INDEKS TIDAK TERMASUK rf_Orders_SalesPeople_OrderDate PADA Sales.Orders (SalespersonPersonID, OrderDate);
Indeks ini mencakup – kueri tidak memerlukan kolom lain untuk mendapatkan jawabannya – dan telah dirancang agar Predikat Pencarian dapat digunakan di SalespersonPersonID, dengan cepat memfilter data ke rentang yang lebih kecil. Fungsi pada OrderDate berarti bahwa dua predikat terakhir tersebut tidak dapat digunakan dalam Predikat Seek, sehingga mereka diturunkan ke Predikat Residual sebagai gantinya. Kueri yang lebih baik akan memfilter tanggal tersebut menggunakan OrderDate>='20130401' AND OrderDate <'20130501', tapi saya membayangkan skenario di sini yang terlalu umum…
Sekarang, jika saya menjalankan kueri, saya dapat melihat dampak dari Predikat Residual. Plan Explorer bahkan memberikan peringatan berguna yang telah saya tulis sebelumnya.
Saya dapat melihat dengan sangat jelas bahwa RangeScan adalah 7.276 baris, dan Predikat Residual memfilternya hingga 149. Plan Explorer menunjukkan informasi lebih lanjut tentang ini di tooltip:
Tetapi tanpa menjalankan kueri, saya tidak dapat melihat informasi itu. Itu tidak ada. Properti dalam rencana perkiraan tidak memilikinya:
Dan saya yakin saya tidak perlu mengingatkan Anda – informasi ini juga tidak ada dalam cache paket. Setelah mengambil paket dari cache menggunakan:
PILIH p.query_plan, t.textFROM sys.dm_exec_cached_plans cCROSS APPLY sys.dm_exec_query_plan(c.plan_handle) pCROSS APPLY sys.dm_exec_sql_text(c.plan_handle) tWHERE tWHERE ' Saya membukanya, dan tentu saja, tidak ada tanda-tanda nilai 7.276 itu. Kelihatannya sama dengan perkiraan rencana yang baru saja saya tunjukkan.Mengeluarkan rencana dari cache adalah tempat nilai perkiraan menjadi miliknya. Bukan hanya karena saya lebih memilih untuk tidak benar-benar menjalankan kueri yang berpotensi mahal di basis data pelanggan. Menanyakan cache paket adalah satu hal, tetapi menjalankan kueri untuk mendapatkan yang sebenarnya – itu jauh lebih sulit.
Dengan SQL 2016 SP1 terinstal, berkat item Connect itu, saya sekarang dapat melihat properti Perkiraan Jumlah Baris yang Akan Dibaca dalam perkiraan rencana, dan dalam cache rencana. Tooltip operator yang ditampilkan di sini diambil dari cache, dan saya dapat dengan mudah melihat bahwa properti Estimasi menampilkan 7.276, serta peringatan residual:
Ini adalah sesuatu yang dapat saya lakukan di kotak pelanggan, mencari di cache untuk situasi dalam rencana bermasalah di mana rasio Perkiraan Jumlah Baris yang Akan Dibaca dan Perkiraan Jumlah Baris tidak bagus. Kemungkinan, seseorang dapat membuat proses yang memeriksa setiap rencana di cache, tetapi itu bukan sesuatu yang telah saya lakukan.
Pembacaan yang cermat akan memperhatikan bahwa Baris Sebenarnya yang keluar dari operator ini adalah 149, yang jauh lebih kecil dari perkiraan 1382,56. Tetapi ketika saya mencari Predikat Residual yang harus memeriksa terlalu banyak baris, rasio 1.382.56 :7.276 masih signifikan.
Sekarang kami telah menemukan bahwa kueri ini tidak efektif bahkan tanpa perlu menjalankannya, cara untuk memperbaikinya adalah dengan memastikan bahwa Predikat Residual cukup SARGable. Permintaan ini…
SELECT COUNT(*) FROM Sales.OrdersWHERE SalespersonPersonID =7 AND OrderDate>='20130401' AND OrderDate <'20130501';…memberikan hasil yang sama, dan tidak memiliki Predikat Residual. Dalam situasi ini, nilai Estimasi Jumlah Baris yang Akan Dibaca identik dengan Estimasi Jumlah Baris, dan inefisiensi hilang:
Seperti yang disebutkan sebelumnya, posting ini adalah bagian dari T-SQL Selasa bulan ini. Mengapa tidak pergi ke sana untuk melihat permintaan fitur lain yang telah diberikan baru-baru ini?