Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Apakah Anda Diurutkan? Tips Mengenai Pemesanan Jendela T-SQL

Indeks pendukung berpotensi membantu menghindari kebutuhan untuk penyortiran eksplisit dalam rencana kueri saat mengoptimalkan kueri T-SQL yang melibatkan fungsi jendela. Dengan indeks pendukung, Maksud saya satu dengan partisi jendela dan elemen pengurutan sebagai kunci indeks, dan kolom lainnya yang muncul dalam kueri sebagai kolom yang disertakan indeks. Saya sering menyebut pola pengindeksan seperti POC indeks sebagai akronim untuk partisi , memesan, dan menutupi . Secara alami, jika elemen partisi atau pengurutan tidak muncul di fungsi jendela, Anda menghilangkan bagian itu dari definisi indeks.

Tetapi bagaimana dengan kueri yang melibatkan beberapa fungsi jendela dengan kebutuhan pemesanan yang berbeda? Demikian pula, bagaimana jika elemen lain dalam kueri selain fungsi jendela juga memerlukan pengaturan data input seperti yang dipesan dalam rencana, seperti klausa ORDER BY presentasi? Hal ini dapat mengakibatkan bagian yang berbeda dari rencana perlu memproses data masukan dalam urutan yang berbeda.

Dalam keadaan seperti itu, Anda biasanya akan menerima penyortiran eksplisit yang tidak dapat dihindari dalam paket. Anda mungkin menemukan susunan sintaksis ekspresi dalam kueri dapat memengaruhi berapa banyak operator sortir eksplisit yang Anda dapatkan dalam paket. Dengan mengikuti beberapa tips dasar, terkadang Anda dapat mengurangi jumlah operator pengurutan eksplisit, yang tentu saja dapat berdampak besar pada kinerja kueri.

Lingkungan untuk Demo

Dalam contoh saya, saya akan menggunakan database sampel PerformanceV5. Anda dapat mengunduh kode sumber untuk membuat dan mengisi database ini di sini.

Saya menjalankan semua contoh di SQL Server 2019 Developer, di mana mode batch di rowstore tersedia.

Dalam artikel ini, saya ingin fokus pada tip yang berkaitan dengan potensi perhitungan fungsi jendela dalam rencana untuk mengandalkan data input yang dipesan tanpa memerlukan aktivitas pengurutan ekstra eksplisit dalam rencana. Ini relevan saat pengoptimal menggunakan perlakuan mode baris serial atau paralel dari fungsi jendela, dan saat menggunakan operator Agregat Jendela mode batch serial.

SQL Server saat ini tidak mendukung kombinasi yang efisien dari input pelestarian urutan paralel sebelum operator Agregat Jendela mode batch paralel. Jadi, untuk menggunakan operator Agregat Jendela mode batch paralel, pengoptimal harus menyuntikkan operator Sortir mode batch paralel perantara, bahkan ketika input sudah dipesan sebelumnya.

Demi kesederhanaan, Anda dapat mencegah paralelisme dalam semua contoh yang ditampilkan dalam artikel ini. Untuk mencapai ini tanpa perlu menambahkan petunjuk ke semua kueri, dan tanpa menyetel opsi konfigurasi seluruh server, Anda dapat menyetel opsi konfigurasi cakupan basis data MAXDOP ke 1 , seperti ini:

USE PerformanceV5;
 
ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 1;

Ingatlah untuk mengaturnya kembali ke 0 setelah Anda selesai menguji contoh di artikel ini. Saya akan mengingatkan Anda di akhir.

Atau, Anda dapat mencegah paralelisme di tingkat sesi dengan DBCC OPTIMIZER_WHATIF yang tidak berdokumen perintah, seperti:

DBCC OPTIMIZER_WHATIF(CPUs, 1);

Untuk menyetel ulang opsi setelah selesai, panggil lagi dengan nilai 0 sebagai jumlah CPU.

Setelah selesai mencoba semua contoh dalam artikel ini dengan paralelisme dinonaktifkan, sebaiknya aktifkan paralelisme dan coba semua contoh lagi untuk melihat perubahan apa yang terjadi.

Kiat 1 dan 2

Sebelum saya memulai dengan tips, pertama-tama mari kita lihat contoh sederhana dengan fungsi jendela yang dirancang untuk memanfaatkan supp class="border indent shadow orting index.

Pertimbangkan kueri berikut, yang akan saya sebut sebagai Kueri 1:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1
 
FROM dbo.Orders;

Jangan khawatir tentang fakta bahwa contoh itu dibuat-buat. Tidak ada alasan bisnis yang baik untuk menghitung total ID pesanan yang berjalan—tabel ini berukuran wajar dengan 1 MM baris, dan saya ingin menunjukkan contoh sederhana dengan fungsi jendela umum seperti yang menerapkan penghitungan total berjalan.

Mengikuti skema pengindeksan POC, Anda membuat indeks berikut untuk mendukung kueri:

CREATE UNIQUE NONCLUSTERED INDEX idx_nc_cid_od_oid ON dbo.Orders(custid, orderdate, orderid);

Rencana untuk kueri ini ditunjukkan pada Gambar 1.

Gambar 1:Rencana untuk Kueri 1

Tidak ada kejutan di sini. Paket menerapkan pemindaian urutan indeks dari indeks yang baru saja Anda buat, menyediakan data yang dipesan ke operator Agregat Jendela, tanpa perlu penyortiran eksplisit.

Selanjutnya, pertimbangkan kueri berikut, yang melibatkan beberapa fungsi jendela dengan kebutuhan pemesanan yang berbeda, serta klausa ORDER BY presentasi:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3
 
FROM dbo.Orders
ORDER BY custid, orderid;

Saya akan menyebut kueri ini sebagai Kueri 2. Rencana kueri ini ditunjukkan pada Gambar 2.

Gambar 2:Rencana untuk Kueri 2

Perhatikan ada empat operator Sortir dalam rencana.

Jika Anda menganalisis berbagai fungsi jendela dan kebutuhan pemesanan presentasi, Anda akan menemukan tiga kebutuhan pemesanan yang berbeda:

  • nomor urut, tanggal pemesanan, id pemesanan
  • sesuai pesanan
  • custid, orderid

Mengingat salah satunya (yang pertama dalam daftar di atas) dapat didukung oleh indeks yang Anda buat sebelumnya, Anda akan berharap untuk melihat hanya dua jenis dalam rencana. Jadi, mengapa rencana memiliki empat macam? Sepertinya SQL Server tidak mencoba terlalu canggih dengan mengatur ulang urutan pemrosesan fungsi dalam rencana untuk meminimalkan pengurutan. Ini memproses fungsi dalam rencana dalam urutan yang muncul dalam kueri. Setidaknya itulah yang terjadi untuk kemunculan pertama dari setiap kebutuhan pemesanan yang berbeda, tetapi saya akan menjelaskannya segera.

Anda dapat menghilangkan kebutuhan semacam itu dalam rencana dengan menerapkan dua praktik sederhana berikut:

Kiat 1:Jika Anda memiliki indeks untuk mendukung beberapa fungsi jendela dalam kueri, tentukan terlebih dahulu.

Kiat 2:Jika kueri melibatkan fungsi jendela dengan kebutuhan pengurutan yang sama seperti pengurutan presentasi dalam kueri, tentukan fungsi tersebut terakhir.

Mengikuti tips ini, Anda mengatur ulang urutan tampilan fungsi jendela dalam kueri seperti ini:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
FROM dbo.Orders
ORDER BY custid, orderid;

Saya akan menyebut kueri ini sebagai Kueri 3. Rencana kueri ini ditunjukkan pada Gambar 3.

Gambar 3:Rencana untuk Kueri 3

Seperti yang Anda lihat, paket tersebut sekarang hanya memiliki dua jenis.

Kiat 3

SQL Server tidak mencoba terlalu canggih dalam mengatur ulang urutan pemrosesan fungsi jendela dalam upaya untuk meminimalkan pengurutan dalam rencana. Namun, ia mampu mengatur ulang sederhana tertentu. Ini memindai fungsi jendela berdasarkan urutan penampilan dalam kueri dan setiap kali mendeteksi kebutuhan pemesanan baru yang berbeda, ia melihat ke depan untuk fungsi jendela tambahan dengan kebutuhan pemesanan yang sama dan jika menemukannya, ia mengelompokkannya bersama dengan kemunculan pertama. Dalam beberapa kasus, ia bahkan dapat menggunakan operator yang sama untuk menghitung beberapa fungsi jendela.

Pertimbangkan kueri berikut sebagai contoh:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
  MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
  AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2
 
FROM dbo.Orders
ORDER BY custid, orderid;

Saya akan menyebut kueri ini sebagai Kueri 4. Rencana untuk kueri ini ditunjukkan pada Gambar 4.

Gambar 4:Rencana untuk Kueri 4

Fungsi jendela dengan kebutuhan pemesanan yang sama tidak dikelompokkan bersama dalam kueri. Namun, masih ada dua jenis dalam rencana. Ini karena apa yang diperhitungkan dalam hal pemrosesan pesanan dalam rencana adalah kemunculan pertama dari setiap kebutuhan pemesanan yang berbeda. Ini membawa saya ke tip ketiga.

Kiat 3:Pastikan untuk mengikuti kiat 1 dan 2 untuk kemunculan pertama setiap kebutuhan pemesanan yang berbeda. Kejadian berikutnya dari kebutuhan pemesanan yang sama, bahkan jika tidak berdekatan, diidentifikasi dan dikelompokkan bersama dengan yang pertama.

Kiat 4 dan 5

Misalkan Anda ingin mengembalikan kolom yang dihasilkan dari perhitungan berjendela dalam urutan kiri-ke-kanan tertentu dalam output. Tetapi bagaimana jika pesanan tidak sama dengan pesanan yang akan meminimalkan pengurutan dalam rencana?

Misalnya, Anda menginginkan hasil yang sama seperti yang dihasilkan oleh Kueri 2 dalam hal urutan kolom kiri-ke-kanan dalam output (urutan kolom:cols lain, sum2, sum1, sum3), tetapi Anda lebih suka memiliki paket yang sama seperti yang Anda dapatkan untuk Kueri 3 (urutan kolom:other cols, sum1, sum3, sum2), yang mendapat dua jenis, bukan empat.

Itu sangat bisa dilakukan jika Anda terbiasa dengan tip keempat.

Kiat 4:Rekomendasi yang disebutkan di atas berlaku untuk urutan tampilan fungsi jendela dalam kode, bahkan jika dalam ekspresi tabel bernama seperti CTE atau tampilan, dan bahkan jika kueri luar mengembalikan kolom dalam urutan yang berbeda dari di ekspresi tabel bernama. Oleh karena itu, jika Anda perlu mengembalikan kolom dalam urutan tertentu dalam output, dan berbeda dari urutan optimal dalam hal meminimalkan pengurutan dalam rencana, ikuti tips dalam hal urutan tampilan dalam ekspresi tabel bernama, dan kembalikan kolom di kueri luar dalam urutan keluaran yang diinginkan.

Kueri berikut, yang akan saya sebut sebagai Kueri 5, menggambarkan teknik ini:

WITH C AS
(
  SELECT orderid, orderdate, custid,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
    SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
  FROM dbo.Orders  
)
SELECT orderid, orderdate, custid,
  sum2, sum1, sum3
FROM C
ORDER BY custid, orderid;

Rencana untuk kueri ini ditunjukkan pada Gambar 5.

Gambar 5:Rencana untuk Kueri 5

Anda masih mendapatkan hanya dua macam dalam rencana meskipun fakta bahwa urutan kolom dalam output adalah:cols lain, sum2, sum1, sum3, seperti di Query 2.

Satu peringatan untuk trik ini dengan ekspresi tabel bernama adalah jika kolom Anda dalam ekspresi tabel tidak direferensikan oleh kueri luar, kolom tersebut dikecualikan dari rencana dan oleh karena itu tidak dihitung.

Pertimbangkan kueri berikut, yang akan saya sebut sebagai Kueri 6:

WITH C AS
(
  SELECT orderid, orderdate, custid,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
    MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
    AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
    SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3
 
  FROM dbo.Orders  
)
SELECT orderid, orderdate, custid,
  sum2, sum1, sum3,
  max2, max1, max3,
  avg2, avg1, avg3
FROM C
ORDER BY custid, orderid;

Di sini semua kolom ekspresi tabel direferensikan oleh kueri luar, jadi pengoptimalan terjadi berdasarkan kemunculan pertama yang berbeda dari setiap kebutuhan pemesanan dalam ekspresi tabel:

  • max1:custid, orderdate, orderid
  • max3:id pesanan
  • max2:custid, orderid

Ini menghasilkan rencana dengan hanya dua macam seperti yang ditunjukkan pada Gambar 6.

Gambar 6:Rencana untuk Kueri 6

Sekarang ubah hanya kueri luar dengan menghapus referensi ke max2, max1, max3, avg2, avg1 dan avg3, seperti:

WITH C AS
(
  SELECT orderid, orderdate, custid,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
    MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
    MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
    AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
    AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
    SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3
 
  FROM dbo.Orders  
)
SELECT orderid, orderdate, custid,
  sum2, sum1, sum3
FROM C
ORDER BY custid, orderid;

Saya akan merujuk ke kueri ini sebagai Kueri 7. Perhitungan max1, max3, max2, avg1, avg3, dan avg2 dalam ekspresi tabel tidak relevan dengan kueri luar sehingga mereka dikecualikan. Komputasi yang tersisa yang melibatkan fungsi jendela dalam ekspresi tabel, yang relevan dengan kueri luar, adalah sum2, sum1, dan sum3. Sayangnya, mereka tidak muncul dalam ekspresi tabel dalam urutan optimal dalam hal meminimalkan jenis. Seperti yang Anda lihat dalam rencana untuk kueri ini seperti yang ditunjukkan pada Gambar 7, ada empat macam.

Gambar 7:Rencana untuk Kueri 7

Jika Anda merasa tidak mungkin memiliki kolom di kueri dalam yang tidak akan Anda rujuk di kueri luar, pikirkan tampilan. Setiap kali Anda mengkueri tampilan, Anda mungkin tertarik pada subkumpulan kolom yang berbeda. Dengan mengingat hal ini, tip kelima dapat membantu mengurangi jenis dalam rencana.

Kiat 5:Dalam kueri dalam dari ekspresi tabel bernama seperti CTE atau tampilan, kelompokkan semua fungsi jendela dengan kebutuhan pengurutan yang sama, dan ikuti kiat 1 dan 2 dalam urutan kelompok fungsi.

Kode berikut mengimplementasikan tampilan berdasarkan rekomendasi ini:

CREATE OR ALTER VIEW dbo.MyView
AS
 
SELECT orderid, orderdate, custid,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS max1,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS avg1,
 
  MAX(orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max3,
 
  SUM(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum3,
 
  AVG(1.0 * orderid) OVER(ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg3,
 
  MAX(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS max2,
 
  AVG(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS avg2,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY ordered ROWS UNBOUNDED PRECEDING) AS sum2
 
FROM dbo.Orders;
GO

Sekarang kueri tampilan yang hanya meminta kolom hasil berjendela sum2, sum1, dan sum3, dalam urutan ini:

SELECT orderid, orderdate, custid,
  sum2, sum1, sum3
FROM dbo.MyView
ORDER BY custid, orderid;

Saya akan menyebut kueri ini sebagai Kueri 8. Anda mendapatkan paket yang ditunjukkan pada Gambar 8 hanya dengan dua jenis.

Gambar 8:Rencanakan untuk Kueri 8

Kiat 6

Saat Anda memiliki kueri dengan beberapa fungsi jendela dengan beberapa kebutuhan pemesanan yang berbeda, kebijaksanaan umum adalah Anda hanya dapat mendukung salah satunya dengan data yang telah dipesan sebelumnya melalui indeks. Hal ini terjadi bahkan ketika semua fungsi jendela memiliki indeks pendukung masing-masing.

Biarkan saya menunjukkan ini. Ingat sebelumnya ketika Anda membuat indeks idx_nc_cid_od_oid, yang dapat mendukung fungsi jendela yang membutuhkan data yang diurutkan berdasarkan custid, orderdate, orderid, seperti ekspresi berikut:

SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING)

Misalkan, selain fungsi jendela ini, Anda juga memerlukan fungsi jendela berikut dalam kueri yang sama:

SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING)

Fungsi jendela ini akan mendapat manfaat dari indeks berikut:

CREATE UNIQUE NONCLUSTERED INDEX idx_nc_cid_oid ON dbo.Orders(custid, orderid);

Kueri berikut, yang akan saya sebut sebagai Kueri 9, memanggil kedua fungsi jendela:

SELECT orderid, orderdate, custid,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1,
 
  SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
FROM dbo.Orders;

Rencana untuk kueri ini ditunjukkan pada Gambar 9.

Gambar 9:Rencanakan untuk Kueri 9

Saya mendapatkan statistik waktu berikut untuk kueri ini di mesin saya, dengan hasil yang dibuang di SSMS:

CPU time = 3234 ms,  elapsed time = 3354 ms.

Seperti dijelaskan sebelumnya, SQL Server memindai ekspresi berjendela sesuai urutan kemunculannya dalam kueri dan angka yang dapat didukungnya yang pertama dengan pemindaian berurutan indeks idx_nc_cid_od_oid. Tapi kemudian itu menambahkan operator Sortir ke rencana untuk memesan data seperti yang dibutuhkan fungsi jendela kedua. Ini berarti rencana tersebut memiliki skala N log N. Itu tidak mempertimbangkan untuk menggunakan indeks idx_nc_cid_oid untuk mendukung fungsi jendela kedua. Anda mungkin berpikir itu tidak bisa, tetapi cobalah untuk berpikir sedikit di luar kebiasaan. Bisakah Anda tidak menghitung setiap fungsi jendela berdasarkan urutan indeks masing-masing dan kemudian menggabungkan hasilnya? Secara teoritis, Anda bisa, dan tergantung pada ukuran data, ketersediaan pengindeksan, dan sumber daya lain yang tersedia, versi gabungan terkadang bisa lebih baik. SQL Server tidak mempertimbangkan pendekatan ini, tetapi Anda pasti dapat mengimplementasikannya dengan menulis join sendiri, seperti:

WITH C1 AS
(
  SELECT orderid, orderdate, custid,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderdate, orderid ROWS UNBOUNDED PRECEDING) AS sum1
 
  FROM dbo.Orders
),
C2 AS
(
  SELECT orderid, custid,
 
    SUM(orderid) OVER(PARTITION BY custid ORDER BY orderid ROWS UNBOUNDED PRECEDING) AS sum2
 
  FROM dbo.Orders
)
SELECT C1.orderid, C1.orderdate, C1.custid, C1.sum1, C2.sum2
FROM C1
  INNER JOIN C2
    ON C1.orderid = C2.orderid;

Saya akan menyebut kueri ini sebagai Kueri 10. Rencana kueri ini ditunjukkan pada Gambar 10.

Gambar 10:Rencana untuk Kueri 10

Paket menggunakan pemindaian berurutan dari dua indeks tanpa penyortiran eksplisit apa pun, menghitung fungsi jendela, dan menggunakan hash join untuk menggabungkan hasilnya. Rencana ini menskalakan secara linier dibandingkan dengan rencana sebelumnya yang memiliki skala N log N.

Saya mendapatkan statistik waktu berikut untuk kueri ini di mesin saya (sekali lagi dengan hasil yang dibuang di SSMS):

CPU time = 1000 ms,  elapsed time = 1100 ms.

Untuk rekap, inilah tip keenam kami.

Kiat 6:Bila Anda memiliki beberapa fungsi jendela dengan beberapa kebutuhan pemesanan yang berbeda, dan Anda dapat mendukung semuanya dengan indeks, coba versi gabungan dan bandingkan kinerjanya dengan kueri tanpa gabungan.

Pembersihan

Jika Anda menonaktifkan paralelisme dengan menyetel opsi konfigurasi cakupan basis data MAXDOP ke 1, aktifkan kembali paralelisme dengan menyetelnya ke 0:

ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 0;

Jika Anda menggunakan opsi sesi tidak berdokumen DBCC OPTIMIZER_WHATIF dengan opsi CPU disetel ke 1, aktifkan kembali paralelisme dengan menyetelnya ke 0:

DBCC OPTIMIZER_WHATIF(CPUs, 0);

Anda dapat mencoba kembali semua contoh dengan paralelisme yang diaktifkan jika Anda mau.

Gunakan kode berikut untuk membersihkan indeks baru yang Anda buat:

DROP INDEX IF EXISTS idx_nc_cid_od_oid ON dbo.Orders;
DROP INDEX IF EXISTS idx_nc_cid_oid ON dbo.Orders;

Dan kode berikut untuk menghapus tampilan:

DROP VIEW IF EXISTS dbo.MyView;

Ikuti Tips untuk Meminimalkan Jumlah Pengurutan

Fungsi window perlu memproses input data yang dipesan. Pengindeksan dapat membantu menghilangkan penyortiran dalam rencana, tetapi biasanya hanya untuk satu kebutuhan pemesanan yang berbeda. Kueri dengan beberapa kebutuhan pemesanan biasanya melibatkan beberapa jenis dalam rencana mereka. Namun, dengan mengikuti tip tertentu, Anda dapat meminimalkan jumlah jenis yang dibutuhkan. Berikut ringkasan tips yang saya sebutkan di artikel ini:

  • Kiat 1: Jika Anda memiliki indeks untuk mendukung beberapa fungsi jendela dalam kueri, tentukan terlebih dahulu.
  • Kiat 2: Jika kueri melibatkan fungsi jendela dengan kebutuhan pengurutan yang sama dengan pengurutan presentasi dalam kueri, tentukan fungsi tersebut terakhir.
  • Kiat 3: Pastikan untuk mengikuti tip 1 dan 2 untuk kemunculan pertama dari setiap kebutuhan pemesanan yang berbeda. Kejadian-kejadian berikutnya dari kebutuhan pemesanan yang sama, bahkan jika tidak berdekatan, diidentifikasi dan dikelompokkan bersama dengan yang pertama.
  • Kiat 4: Rekomendasi yang disebutkan di atas berlaku untuk urutan tampilan fungsi jendela dalam kode, bahkan jika dalam ekspresi tabel bernama seperti CTE atau tampilan, dan bahkan jika kueri luar mengembalikan kolom dalam urutan yang berbeda dari pada ekspresi tabel bernama. Oleh karena itu, jika Anda perlu mengembalikan kolom dalam urutan tertentu dalam output, dan berbeda dari urutan optimal dalam hal meminimalkan pengurutan dalam rencana, ikuti tips dalam hal urutan tampilan dalam ekspresi tabel bernama, dan kembalikan kolom dalam kueri luar dalam urutan keluaran yang diinginkan.
  • Kiat 5: Dalam kueri bagian dalam dari ekspresi tabel bernama seperti CTE atau tampilan, kelompokkan semua fungsi jendela dengan kebutuhan pemesanan yang sama, dan ikuti tip 1 dan 2 dalam urutan grup fungsi.
  • Kiat 6: Saat Anda memiliki beberapa fungsi jendela dengan beberapa kebutuhan pemesanan yang berbeda, dan Anda dapat mendukung semuanya dengan indeks, coba versi gabungan dan bandingkan kinerjanya dengan kueri tanpa gabungan.

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Penggabungan Nested Loops dan Performance Spool

  2. Menggunakan Fungsi DATEADD, DATEDIFF dan DATEPART T-SQL dalam Istilah Sederhana

  3. Cara Menginstal ArangoDB di Ubuntu 20.04

  4. SQL ATAU Operator untuk Pemula

  5. Bukan Anda, ini saya (pemecahan masalah I/O)