Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Performa agregasi bersyarat

Ringkasan singkat

  • Kinerja metode subquery bergantung pada distribusi data.
  • Kinerja agregasi bersyarat tidak bergantung pada distribusi data.

Metode subqueries bisa lebih cepat atau lebih lambat dari agregasi bersyarat, itu tergantung pada distribusi data.

Secara alami, jika tabel memiliki indeks yang sesuai, maka subquery kemungkinan akan mendapat manfaat darinya, karena indeks akan memungkinkan untuk memindai hanya bagian yang relevan dari tabel, bukan pemindaian penuh. Memiliki indeks yang sesuai tidak mungkin secara signifikan menguntungkan metode agregasi Bersyarat, karena tetap akan memindai indeks penuh. Satu-satunya keuntungan adalah jika indeks lebih sempit dari tabel dan mesin harus membaca lebih sedikit halaman ke dalam memori.

Mengetahui hal ini, Anda dapat memutuskan metode mana yang akan dipilih.

Tes pertama

Saya membuat tabel pengujian yang lebih besar, dengan 5 juta baris. Tidak ada indeks di atas meja. Saya mengukur statistik IO dan CPU menggunakan SQL Sentry Plan Explorer. Saya menggunakan SQL Server 2014 SP1-CU7 (12.0.4459.0) Express 64-bit untuk pengujian ini.

Memang, kueri asli Anda berperilaku seperti yang Anda jelaskan, yaitu subkueri lebih cepat meskipun pembacaannya 3 kali lebih tinggi.

Setelah beberapa percobaan pada tabel tanpa indeks, saya menulis ulang agregat bersyarat Anda dan menambahkan variabel untuk menyimpan nilai DATEADD ekspresi.

Secara keseluruhan waktu menjadi jauh lebih cepat.

Kemudian saya mengganti SUM dengan COUNT dan menjadi sedikit lebih cepat lagi.

Bagaimanapun, agregasi bersyarat menjadi secepat subquery.

Hangatkan cache (CPU=375)

SELECT -- warm cache
    COUNT(*) AS all_cnt
FROM LogTable
OPTION (RECOMPILE);

Subkueri (CPU=1031)

SELECT -- subqueries
(
    SELECT count(*) FROM LogTable 
) all_cnt, 
(
    SELECT count(*) FROM LogTable WHERE datesent > DATEADD(year,-1,GETDATE())
) last_year_cnt,
(
    SELECT count(*) FROM LogTable WHERE datesent > DATEADD(year,-10,GETDATE())
) last_ten_year_cnt
OPTION (RECOMPILE);

Agregasi bersyarat asli (CPU=1641)

SELECT -- conditional original
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > DATEADD(year,-1,GETDATE())
             THEN 1 ELSE 0 END) AS last_year_cnt,
    SUM(CASE WHEN datesent > DATEADD(year,-10,GETDATE())
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Agregasi bersyarat dengan variabel (CPU=1078)

DECLARE @VarYear1 datetime = DATEADD(year,-1,GETDATE());
DECLARE @VarYear10 datetime = DATEADD(year,-10,GETDATE());

SELECT -- conditional variables
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > @VarYear1
             THEN 1 ELSE 0 END) AS last_year_cnt,
    SUM(CASE WHEN datesent > @VarYear10
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Agregasi bersyarat dengan variabel dan COUNT, bukan SUM (CPU=1062)

SELECT -- conditional variable, count, not sum
    COUNT(*) AS all_cnt,
    COUNT(CASE WHEN datesent > @VarYear1
             THEN 1 ELSE NULL END) AS last_year_cnt,
    COUNT(CASE WHEN datesent > @VarYear10
             THEN 1 ELSE NULL END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Berdasarkan hasil ini, tebakan saya adalah CASE dipanggil DATEADD untuk setiap baris, sedangkan WHERE cukup pintar untuk menghitungnya sekali. Ditambah COUNT sedikit lebih efisien daripada SUM .

Pada akhirnya, agregasi bersyarat hanya sedikit lebih lambat dari subquery (1062 vs 1031), mungkin karena WHERE sedikit lebih efisien daripada CASE itu sendiri, dan selain itu, WHERE menyaring beberapa baris, jadi COUNT harus memproses lebih sedikit baris.

Dalam praktiknya saya akan menggunakan agregasi bersyarat, karena menurut saya jumlah bacaan lebih penting. Jika tabel Anda kecil agar muat dan tetap berada di kumpulan buffer, maka kueri apa pun akan cepat untuk pengguna akhir. Namun, jika tabel lebih besar dari memori yang tersedia, maka saya berharap membaca dari disk akan memperlambat subkueri secara signifikan.

Ujian kedua

Di sisi lain, memfilter baris sedini mungkin juga penting.

Berikut adalah sedikit variasi dari tes, yang menunjukkannya. Di sini saya menetapkan ambang batas menjadi GETDATE() + 100 tahun, untuk memastikan tidak ada baris yang memenuhi kriteria filter.

Hangatkan cache (CPU=344)

SELECT -- warm cache
    COUNT(*) AS all_cnt
FROM LogTable
OPTION (RECOMPILE);

Subkueri (CPU=500)

SELECT -- subqueries
(
    SELECT count(*) FROM LogTable 
) all_cnt, 
(
    SELECT count(*) FROM LogTable WHERE datesent > DATEADD(year,100,GETDATE())
) last_year_cnt
OPTION (RECOMPILE);

Agregasi bersyarat asli (CPU=937)

SELECT -- conditional original
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > DATEADD(year,100,GETDATE())
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Agregasi bersyarat dengan variabel (CPU=750)

DECLARE @VarYear100 datetime = DATEADD(year,100,GETDATE());

SELECT -- conditional variables
    COUNT(*) AS all_cnt,
    SUM(CASE WHEN datesent > @VarYear100
             THEN 1 ELSE 0 END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Agregasi bersyarat dengan variabel dan COUNT, bukan SUM (CPU=750)

SELECT -- conditional variable, count, not sum
    COUNT(*) AS all_cnt,
    COUNT(CASE WHEN datesent > @VarYear100
             THEN 1 ELSE NULL END) AS last_ten_year_cnt
FROM LogTable
OPTION (RECOMPILE);

Di bawah ini adalah rencana dengan subquery. Anda dapat melihat bahwa 0 baris masuk ke Agregat Aliran di subkueri kedua, semuanya disaring pada langkah Pemindaian Tabel.

Hasilnya, subquery kembali lebih cepat.

Tes ketiga

Di sini saya mengubah kriteria penyaringan dari tes sebelumnya:semua > diganti dengan < . Akibatnya, COUNT bersyarat menghitung semua baris, bukan tidak ada. Kejutan kejutan! Kueri agregasi bersyarat membutuhkan waktu 750 mdtk yang sama, sedangkan subkueri menjadi 813, bukan 500.

Berikut adalah rencana untuk subquery:

Bisakah Anda memberi saya sebuah contoh, di mana agregasi bersyarat terutama mengungguli solusi subquery?

Ini dia. Kinerja metode subqueries tergantung pada distribusi data. Performa agregasi bersyarat tidak bergantung pada distribusi data.

Metode subqueries bisa lebih cepat atau lebih lambat dari agregasi bersyarat, itu tergantung pada distribusi data.

Mengetahui hal ini, Anda dapat memutuskan metode mana yang akan dipilih.

Detail bonus

Jika Anda mengarahkan mouse ke Table Scan operator Anda dapat melihat Actual Data Size dalam berbagai varian.

  1. Sederhana COUNT(*) :

  1. Agregasi bersyarat:

  1. Subquery dalam pengujian 2:

  1. Subquery dalam pengujian 3:

Sekarang menjadi jelas bahwa perbedaan kinerja kemungkinan disebabkan oleh perbedaan jumlah data yang mengalir melalui rencana.

Dalam kasus COUNT(*) simple sederhana tidak ada Output list (tidak diperlukan nilai kolom) dan ukuran data terkecil (43MB).

Dalam hal agregasi bersyarat, jumlah ini tidak berubah antara pengujian 2 dan 3, selalu 72MB. Output list memiliki satu kolom datesent .

Dalam hal subkueri, jumlah ini tidak berubah tergantung pada distribusi data.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Fungsi vs Prosedur Tersimpan

  2. SQL pilih di mana tidak dalam subquery tidak mengembalikan hasil

  3. Bagaimana cara memeriksa kompatibilitas Database SQL Server setelah sp_dbcmptlevel tidak digunakan lagi?

  4. Kondisi Balapan Antrian Proses SQL Server

  5. Node.js dan Microsoft SQL Server