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

subquery atau leftjoin dengan grup mana yang lebih cepat?

Sumber yang bagus untuk menghitung total berjalan di SQL Server adalah dokumen ini oleh Itzik Ben Gan yang dikirimkan ke Tim SQL Server sebagai bagian dari kampanyenya untuk memiliki OVER klausa diperpanjang lebih jauh dari implementasi awal SQL Server 2005. Di dalamnya ia menunjukkan bagaimana begitu Anda masuk ke puluhan ribu baris, kursor keluar melakukan solusi berbasis set. SQL Server 2012 memang memperpanjang OVER klausa membuat kueri semacam ini menjadi lebih mudah.

SELECT col1,
       SUM(col1) OVER (ORDER BY ind ROWS UNBOUNDED PRECEDING)
FROM   @tmp 

Karena Anda menggunakan SQL Server 2005 namun ini tidak tersedia untuk Anda.

Adam Machanic tampilkan di sini bagaimana CLR dapat digunakan untuk meningkatkan kinerja kursor TSQL standar.

Untuk definisi tabel ini

CREATE TABLE RunningTotals
(
ind int identity(1,1) primary key,
col1 int
)

Saya membuat tabel dengan 2.000 dan 10.000 baris dalam database dengan ALLOW_SNAPSHOT_ISOLATION ON dan satu dengan pengaturan ini (Alasan untuk ini adalah karena hasil awal saya berada di DB dengan pengaturan pada yang menyebabkan aspek membingungkan dari hasil).

Indeks berkerumun untuk semua tabel hanya memiliki 1 halaman root. Jumlah halaman daun untuk masing-masing halaman ditunjukkan di bawah ini.

+-------------------------------+-----------+------------+
|                               | 2,000 row | 10,000 row |
+-------------------------------+-----------+------------+
| ALLOW_SNAPSHOT_ISOLATION OFF  |         5 |         22 |
| ALLOW_SNAPSHOT_ISOLATION ON   |         8 |         39 |
+-------------------------------+-----------+------------+

Saya menguji kasus berikut (Tautan menunjukkan rencana eksekusi)

  1. Kiri Bergabung dan Grup Oleh
  2. Subkueri terkait Paket baris 2000 ,Paket 10.000 baris
  3. CTE dari jawaban Mikael (diperbarui)
  4. CTE di bawah

Alasan dimasukkannya opsi CTE tambahan adalah untuk memberikan solusi CTE yang akan tetap berfungsi jika ind kolom tidak dijamin berurutan.

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
DECLARE @col1 int, @sumcol1 bigint;

WITH    RecursiveCTE
AS      (
        SELECT TOP 1 ind, col1, CAST(col1 AS BIGINT) AS Total
        FROM RunningTotals
        ORDER BY ind
        UNION   ALL
        SELECT  R.ind, R.col1, R.Total
        FROM    (
                SELECT  T.*,
                        T.col1 + Total AS Total,
                        rn = ROW_NUMBER() OVER (ORDER BY T.ind)
                FROM    RunningTotals T
                JOIN    RecursiveCTE R
                        ON  R.ind < T.ind
                ) R
        WHERE   R.rn = 1
        )
SELECT  @col1 =col1, @sumcol1=Total
FROM    RecursiveCTE
OPTION  (MAXRECURSION 0);

Semua kueri memiliki CAST(col1 AS BIGINT) ditambahkan untuk menghindari kesalahan overflow saat runtime. Selain itu untuk semuanya, saya menetapkan hasil ke variabel seperti di atas untuk menghilangkan waktu yang dihabiskan untuk mengirim kembali hasil dari pertimbangan.

Hasil

+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
|                  |          |        |          Base Table        |         Work Table         |     Time        |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
|                  | Snapshot | Rows   | Scan count | logical reads | Scan count | logical reads | cpu   | elapsed |
| Group By         | On       | 2,000  | 2001       | 12709         |            |               | 1469  | 1250    |
|                  | On       | 10,000 | 10001      | 216678        |            |               | 30906 | 30963   |
|                  | Off      | 2,000  | 2001       | 9251          |            |               | 1140  | 1160    |
|                  | Off      | 10,000 | 10001      | 130089        |            |               | 29906 | 28306   |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| Sub Query        | On       | 2,000  | 2001       | 12709         |            |               | 844   | 823     |
|                  | On       | 10,000 | 2          | 82            | 10000      | 165025        | 24672 | 24535   |
|                  | Off      | 2,000  | 2001       | 9251          |            |               | 766   | 999     |
|                  | Off      | 10,000 | 2          | 48            | 10000      | 165025        | 25188 | 23880   |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| CTE No Gaps      | On       | 2,000  | 0          | 4002          | 2          | 12001         | 78    | 101     |
|                  | On       | 10,000 | 0          | 20002         | 2          | 60001         | 344   | 342     |
|                  | Off      | 2,000  | 0          | 4002          | 2          | 12001         | 62    | 253     |
|                  | Off      | 10,000 | 0          | 20002         | 2          | 60001         | 281   | 326     |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| CTE Alllows Gaps | On       | 2,000  | 2001       | 4009          | 2          | 12001         | 47    | 75      |
|                  | On       | 10,000 | 10001      | 20040         | 2          | 60001         | 312   | 413     |
|                  | Off      | 2,000  | 2001       | 4006          | 2          | 12001         | 94    | 90      |
|                  | Off      | 10,000 | 10001      | 20023         | 2          | 60001         | 313   | 349     |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+

Baik subquery yang berkorelasi maupun GROUP BY versi menggunakan gabungan loop bersarang "segitiga" yang didorong oleh pemindaian indeks berkerumun pada RunningTotals tabel (T1 ) dan, untuk setiap baris yang dikembalikan oleh pemindaian itu, mencari kembali ke dalam tabel (T2 ) bergabung sendiri di T2.ind<=T1.ind .

Ini berarti bahwa baris yang sama diproses berulang kali. Ketika T1.ind=1000 baris diproses, self join mengambil dan menjumlahkan semua baris dengan ind <= 1000 , lalu untuk baris berikutnya di mana T1.ind=1001 1000 baris yang sama diambil lagi dan dijumlahkan dengan satu baris tambahan dan seterusnya.

Jumlah total operasi tersebut untuk tabel 2.000 baris adalah 2.001.000, untuk 10k baris 50.000.000 atau lebih umum (n² + n) / 2 yang jelas tumbuh secara eksponensial.

Dalam kasus 2.000 baris perbedaan utama antara GROUP BY dan versi subquery adalah bahwa yang pertama memiliki agregat aliran setelah bergabung dan memiliki tiga kolom yang dimasukkan ke dalamnya (T1.ind , T2.col1 , T2.col1 ) dan GROUP BY milik T1.ind sedangkan yang terakhir dihitung sebagai agregat skalar, dengan agregat aliran sebelum bergabung, hanya memiliki T2.col1 memasukkannya ke dalamnya dan tidak memiliki GROUP BY properti ditetapkan sama sekali. Pengaturan yang lebih sederhana ini dapat dilihat memiliki manfaat yang terukur dalam hal pengurangan waktu CPU.

Untuk kasus 10.000 baris ada perbedaan tambahan dalam rencana sub kueri. Itu menambahkan spool eager yang menyalin semua ind,cast(col1 as bigint) nilai ke dalam tempdb . Dalam hal isolasi snapshot pada ini bekerja lebih kompak daripada struktur indeks berkerumun dan efek bersihnya adalah mengurangi jumlah pembacaan sekitar 25% (karena tabel dasar mempertahankan cukup banyak ruang kosong untuk info versi), ketika opsi ini dimatikan, hasilnya kurang ringkas (mungkin karena bigint vs int perbedaan) dan lebih banyak membaca hasil. Ini mengurangi kesenjangan antara subkueri dan grup menurut versi, tetapi subkueri tetap menang.

Namun pemenang yang jelas adalah CTE Rekursif. Untuk versi "tanpa celah" pembacaan logis dari tabel dasar sekarang 2 x (n + 1) mencerminkan n index mencari ke dalam indeks 2 level untuk mengambil semua baris ditambah yang tambahan di akhir yang tidak mengembalikan apa pun dan menghentikan rekursi. Itu masih berarti 20.002 pembacaan untuk memproses tabel 22 halaman!

Pembacaan tabel kerja logis untuk versi CTE rekursif sangat tinggi. Tampaknya berhasil pada 6 pembacaan meja kerja per baris sumber. Ini berasal dari spool indeks yang menyimpan output dari baris sebelumnya kemudian dibaca lagi di iterasi berikutnya (penjelasan yang bagus tentang ini oleh Umachandar Jayachandran di sini ). Meskipun angkanya tinggi, ini tetap yang terbaik.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SELECT * INTO mempertahankan ORDER BY di SQL Server 2008 tetapi tidak 2012

  2. Masukkan ke dalam tabel dengan pernyataan pilih ditambah kolom tambahan di SQL Server 2008

  3. Cara membuat tabel dari hasil kueri pemilihan di SQL Server 2008

  4. Kesalahan:Harus mendeklarasikan variabel skalar untuk menyisipkan pernyataan di banyak basis data

  5. Cari Tahu Sistem Operasi yang Menjalankan SQL Server (Contoh T-SQL)