Membandingkan dua kueri di SQL Server 2012

14

Saya membandingkan dua kueri di SQL Server 2012. Tujuannya adalah untuk menggunakan semua informasi terkait yang tersedia dari pengoptimal kueri saat memilih kueri terbaik. Kedua pertanyaan menghasilkan hasil yang sama; pesanan maksimum untuk semua pelanggan.

Membersihkan kumpulan buffer dilakukan sebelum menjalankan setiap permintaan dengan FREEPROCCACHE dan DROPCLEANBUFFERS

Menggunakan informasi yang disediakan di bawah ini, pertanyaan apa yang merupakan pilihan yang lebih baik?

-- Query 1 - return the maximum order id for a customer
SELECT orderid, custid
FROM Sales.Orders AS O1
WHERE orderid = (SELECT MAX(O2.orderid)
                 FROM Sales.Orders AS O2
                 WHERE O2.custid = O1.custid);


-- Query 2 - return the maximum order id for a customer
SELECT MAX(orderid), custid
FROM Sales.Orders AS O1
group by custid
order by custid

WAKTU STATISTIK

Kueri 1 WAKTU STATISTIK: Waktu CPU = 0ms, waktu yang berlalu = 24 ms

Kueri 2 WAKTU STATISTIK: Waktu CPU = 0 ms, waktu yang berlalu = 23 ms

STATISTIK IO

Kueri 1 STATISTIK IO: Tabel 'Pesanan'. Pindai hitungan 1, bacaan logis 5, bacaan fisik 2, bacaan baca depan 0, bacaan logis lob 0, bacaan fisik lob 0, bacaan baca lob depan 0.

Kueri 2 STATISTIK IO: Tabel 'Pesanan'. Pindai hitungan 1, bacaan logis 4, bacaan fisik 1, bacaan baca-depan 8, bacaan logis lob 0, bacaan fisik lob 0, bacaan lob baca-depan 0.

Rencana Eksekusi

masukkan deskripsi gambar di sini

SELECT properties Permintaan 1

masukkan deskripsi gambar di sini

SELECT properties Query 2

masukkan deskripsi gambar di sini

Kesimpulan:

Pertanyaan 1

  1. Biaya batch 48%
  2. Bacaan Logis 5
  3. Bacaan Fisik 2
  4. Baca-depan Dibaca: 0
  5. Waktu CPU: 0ms
  6. Waktu Berlalu 24ms
  7. Perkiraan biaya subtree: 0,0050276
  8. CompileCPU: 2
  9. CompileMemory: 384
  10. CompileTime: 2

Pertanyaan 2

  1. Biaya batch 52%
  2. Bacaan Logis 4
  3. Bacaan Fisik 1
  4. Baca-depan Dibaca: 8
  5. Waktu CPU 0
  6. Waktu Berlalu 23ms
  7. Perkiraan biaya subtree: 0,0054782
  8. CompileCPU: 0
  9. CompileMemory: 192
  10. CompileTime: 0

Secara pribadi, meskipun Permintaan 2 memiliki biaya batch yang lebih tinggi sesuai dengan rencana grafis, saya pikir ini lebih efisien daripada Permintaan 1. Ini karena permintaan 2 membutuhkan pembacaan yang kurang logis, memiliki waktu berlalu yang sedikit lebih rendah, nilai compilecpu, compilememory dan compiletime adalah menurunkan. baca baca depan adalah 8 untuk kueri 2 dan 0 untuk kueri 1.

Perbarui 12:03

Definisi Indeks Berkerumun

ALTER TABLE [Sales].[Orders] ADD  CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
    [orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

Indeks Non-Clustered idx_nc_custid

CREATE NONCLUSTERED INDEX [idx_nc_custid] ON [Sales].[Orders]
(
    [custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Craig Efrein
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Paul White 9

Jawaban:

10

Saya suka pendekatan Anda dengan pertimbangan yang cermat untuk mencari opsi dan paket yang dicari. Saya berharap lebih banyak pengembang melakukan ini. Satu peringatan akan - selalu menguji dengan banyak baris, melihat bacaan logis, ini adalah tabel bertubuh kecil. Coba dan hasilkan sampel load dan jalankan kueri lagi. Satu masalah kecil - di kueri teratas Anda, Anda tidak meminta pesanan oleh, di kueri bawah Anda. Anda harus membandingkan dan membedakan mereka masing-masing dengan pemesanan.

Saya baru saja dengan cepat membuat tabel SalesOrders dengan 200.000 pesanan penjualan di dalamnya - masih tidak besar dengan imajinasi. Dan menjalankan kueri dengan ORDER OLEH di masing-masing. Saya juga bermain dengan indeks sedikit.

Tanpa indeks berkerumun di OrderID, hanya indeks non-berkerumun di CustID Kueri kedua mengungguli. Apalagi dengan urutan yang termasuk dalam masing-masing. Ada dua kali lebih banyak bacaan pada kueri pertama dari kueri kedua, dan persentase biaya adalah 67% / 33% antara kueri.

Dengan indeks berkerumun di OrderID dan indeks non-berkerumun di CustID, mereka melakukan dalam kecepatan yang sama dan jumlah pembacaan yang sama persis.

Jadi saya sarankan Anda meningkatkan jumlah baris dan melakukan beberapa pengujian lagi. Tapi analisis terakhir saya pada pertanyaan Anda -

Anda mungkin menemukan mereka berperilaku lebih mirip daripada yang Anda sadari ketika Anda meningkatkan baris, jadi ingatlah peringatan itu dan uji seperti itu.

Jika semua yang Anda ingin kembalikan adalah OrderID maksimum untuk setiap Pelanggan, dan Anda ingin menentukan bahwa dengan OrderID menjadi OrderID terbesar maka permintaan kedua dari keduanya adalah cara terbaik untuk keluar dari pola pikir saya - ini sedikit lebih sederhana dan walaupun sedikit lebih mahal berdasarkan biaya subtree itu adalah pernyataan yang lebih cepat dan lebih mudah untuk menguraikan. Jika Anda berniat menambahkan kolom lain ke dalam set hasil Anda suatu hari nanti? Maka permintaan pertama memungkinkan Anda melakukan itu.

Diperbarui: Salah satu komentar Anda di bawah pertanyaan Anda adalah:

Harap diingat, bahwa menemukan kueri terbaik dalam pertanyaan ini adalah cara untuk menyempurnakan teknik yang digunakan untuk membandingkannya.

Tapi takeaway terbaik untuk melakukan itu- uji dengan lebih banyak data - selalu memastikan Anda memiliki data yang konsisten dengan produksi dan produksi yang diharapkan di masa depan. Paket kueri mulai mencari data saat Anda memberikan lebih banyak baris ke tabel, dan mencoba dan menjaga distribusi apa yang Anda harapkan dalam produksi. Dan perhatikan hal-hal seperti termasuk Order By atau tidak, di sini saya tidak berpikir itu membuat sedikit perbedaan pada akhirnya, tetapi masih layak untuk digali.

Pendekatan Anda membandingkan tingkat detail dan data ini bagus. Sebagian besar biaya subtree sewenang-wenang dan tidak berarti, tetapi masih layak setidaknya mencari perbandingan antara pengeditan / perubahan atau bahkan antara permintaan. Melihat statistik waktu dan IO cukup penting, seperti juga melihat rencana untuk apa pun yang terasa tidak pada tempatnya untuk ukuran data yang Anda kerjakan dan apa yang Anda coba lakukan.

Mike Walsh
sumber
Halo lagi, terima kasih atas poin Anda tentang penggunaan volume data yang lebih besar. Ini bukan pertama kalinya seseorang mengungkitnya. Terakhir kali meskipun itu untuk mempertimbangkan kemungkinan fragmentasi dari pemisahan halaman. Dalam sampel 200.000 baris Anda, apakah Anda memeriksa fragmentasi?
Craig Efrein
Nah dalam contoh baris 200k cepat kecil saya, saya tidak fokus pada fragmentasi, tidak. Tapi cara saya melakukannya tidak akan ada. Saya membuat tabel, mengisinya dan kemudian membuat indeks, Jadi mereka baru dibuat indeks. Dan itu tidak akan mengubah pendekatan melihat rencana kueri yang tampaknya menjadi pertanyaan utama. Volume data besar - sangat besar - dalam melihat rencana kueri secara akurat. Saya sering melihat kasus-kasus di mana itu tampak hebat di dev (dengan 1-10 baris) dan sangat mengerikan dengan data nyata. Tetapi pendekatan Anda baik dan mudah-mudahan info ini dan percakapan dalam komentar membantu
Mike Walsh
Karena kita dikelompokkan berdasarkan custid, bagaimana Anda membuat nilai custid cukup acak? Satu hal yang saya ingat dari bacaan saya, adalah pentingnya nilai-nilai yang berbeda. Jika custid hanya memiliki sejumlah kecil pelanggan yang berbeda, maka biaya untuk agregat aliran tidak realistis.
Craig Efrein
Saya hanya menggunakan fungsi RAND untuk membuat 100 pelanggan dan secara acak menetapkan satu untuk setiap pesanan ID .. Saya sedang melakukan pemeriksaan cepat. :)
Mike Walsh
Terima kasih Mike atas semua bantuan Anda. Tapi satu pertanyaan terakhir. Dari layar properti SELECT dari Execution Plan pada 2012 yang saya berikan dalam pertanyaan saya, nilai apa yang Anda perhatikan?
Craig Efrein