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

Tantangan T-SQL Kepulauan

Baru-baru ini, saya diperkenalkan dengan tantangan pulau baru oleh teman saya Erland Sommarskog. Ini didasarkan pada pertanyaan dari forum database publik. Tantangannya sendiri tidak rumit untuk ditangani saat menggunakan teknik terkenal, yang terutama menggunakan fungsi jendela. Namun, teknik ini memerlukan penyortiran eksplisit meskipun ada indeks pendukung. Hal ini mempengaruhi skalabilitas dan waktu respon dari solusi. Menyukai tantangan, saya mulai mencari solusi untuk meminimalkan jumlah operator Sortir eksplisit dalam rencana, atau lebih baik lagi, menghilangkan kebutuhan untuk itu sama sekali. Dan saya menemukan solusi seperti itu.

Saya akan mulai dengan menyajikan bentuk tantangan yang digeneralisasikan. Saya kemudian akan menunjukkan dua solusi berdasarkan teknik yang diketahui, diikuti oleh solusi baru. Terakhir, saya akan membandingkan kinerja berbagai solusi.

Saya sarankan Anda mencoba mencari solusi sebelum menerapkan solusi saya.

Tantangan

Saya akan menyajikan bentuk umum dari tantangan pulau asli Erland.

Gunakan kode berikut untuk membuat tabel bernama T1 dan mengisinya dengan sekumpulan kecil sampel data:

ATUR NOCOUNT AKTIF; GUNAKAN tempdb; DROP TABLE JIKA ADA dbo.T1 CREATE TABLE dbo.T1( grp VARCHAR(10) NOT NULL, ord INT NOT NULL, val VARCHAR(10) NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY(grp, ord));GO INSERT INTO dbo. T1(grp, ord, val) NILAI ('Grup A', 1002, 'Y'), ('Grup A', 1003, 'Y'), ('Grup A', 1005, 'Y'), (' Grup A', 1007, 'N'), ('Grup A', 1011, 'N'), ('Grup A', 1013, 'N'), ('Grup A', 1017, 'Y'), ('Grup A', 1019, 'Y'), ('Grup A', 1023, 'N'), ('Grup A', 1029, 'N'), ('Grup B', 1001, 'X' ), ('Grup B', 1002, 'X'), ('Grup B', 1003, 'Z'), ('Grup B', 1005, 'Z'), ('Grup B', 1008, ' Z'), ('Grup B', 1013, 'Z'), ('Grup B', 1021, 'Y'), ('Grup B', 1034, 'Y');

Tantangannya adalah sebagai berikut:

Dengan asumsi partisi berdasarkan grp kolom dan pengurutan berdasarkan urutan kolom, hitung nomor baris berurutan yang dimulai dengan 1 dalam setiap grup baris berurutan dengan nilai yang sama di kolom val. Berikut adalah hasil yang diinginkan untuk kumpulan kecil data sampel yang diberikan:

grp ord val seqno-------- ----- ---- ------Grup A 1002 Y 1Grup A 1003 Y 2Grup A 1005 Y 3Grup A 1007 N 1Grup A 1011 N 2Grup A 1013 N 3Grup A 1017 Y 1Grup A 1019 Y 2Grup A 1023 N 1Grup A 1029 N 2Grup B 1001 X 1Grup B 1002 X 2Grup B 1003 Z 1Grup B 1005 Z 2Grup B 1008 Z 3Grup B 1013 Z 4Grup B 1021 Y 1 1034 Y 2

Perhatikan definisi batasan kunci utama berdasarkan kunci komposit (grp, ord), yang menghasilkan indeks berkerumun berdasarkan kunci yang sama. Indeks ini berpotensi mendukung fungsi jendela yang dipartisi oleh grp dan diurutkan berdasarkan ord.

Untuk menguji kinerja solusi Anda, Anda harus mengisi tabel dengan volume data sampel yang lebih besar. Gunakan kode berikut untuk membuat fungsi pembantu GetNums:

CREATE FUNCTION dbo.GetNums(@low AS BIGINT =1, @high AS BIGINT) MENGEMBALIKAN TABLEASRETURN DENGAN L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1),(1),(1), (1),(1),(1),(1), (1),(1),(1),(1),(1),(1),(1),(1)) AS D (c) ), L1 AS ( PILIH 1 AS c DARI L0 SEBAGAI LINTAS GABUNG L0 AS B ), L2 AS ( PILIH 1 AS c DARI L1 SEBAGAI LINTAS GABUNG L1 AS B ), L3 AS ( PILIH 1 AS c DARI L2 AS A CROSS JOIN L2 AS B ), Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM L3 ) SELECT TOP(@high - @low + 1) rownum AS rn, @high + 1 - rownum AS op, @low - 1 + rownum AS n FROM Nums ORDER BY rownum;GO

Gunakan kode berikut untuk mengisi T1, setelah mengubah parameter yang mewakili jumlah grup, jumlah baris per grup, dan jumlah nilai yang berbeda sesuai keinginan:

DECLARE @numgroups AS INT =1000, @rowspergroup AS INT =10000, -- uji dengan 1000 hingga 10000 di sini @uniquevalues ​​AS INT =5; ALTER TABLE dbo.T1 DROP CONSTRAINT PK_T1; TRUNCATE TABEL dbo.T1; INSERT INTO dbo.T1 WITH(TABLOCK) (grp, ord, val) SELECT CAST(G.n AS VARCHAR(10)) AS grp, CAST(R.n AS INT) AS ord, CAST(ABS(CHECKSUM(NEWID())) % @uniquevalues ​​+ 1 SEBAGAI VARCHAR(10)) SEBAGAI val DARI dbo.GetNums(1, @numgroups) SEBAGAI G CROSS GABUNG dbo.GetNums(1, @rowspergroup) AS R; ALTER TABLE dbo.T1 ADD CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED(grp, ord);

Dalam pengujian kinerja saya, saya mengisi tabel dengan 1.000 grup, antara 1.000 dan 10.000 baris per grup (jadi 1 juta hingga 10 juta baris), dan 5 nilai berbeda. Saya menggunakan SELECT INTO pernyataan untuk menulis hasilnya ke dalam tabel sementara.

Mesin uji saya memiliki empat CPU logis, menjalankan SQL Server 2019 Enterprise.

Saya akan menganggap Anda menggunakan lingkungan yang dirancang untuk mendukung mode batch pada penyimpanan baris baik secara langsung, misalnya, menggunakan edisi SQL Server 2019 Enterprise seperti milik saya, atau secara tidak langsung, dengan membuat indeks penyimpanan kolom dummy di atas meja.

Ingat, poin ekstra jika Anda berhasil menemukan solusi yang efisien tanpa penyortiran eksplisit dalam rencana. Semoga berhasil!

Apakah operator Sortir diperlukan dalam optimalisasi fungsi jendela?

Sebelum saya membahas solusi, sedikit latar belakang pengoptimalan sehingga apa yang akan Anda lihat di rencana kueri nanti akan lebih masuk akal.

Teknik yang paling umum untuk menyelesaikan tugas pulau seperti kita melibatkan penggunaan beberapa kombinasi fungsi jendela agregat dan/atau peringkat. SQL Server dapat memproses fungsi jendela seperti itu menggunakan serangkaian operator mode baris yang lebih lama (Segmen, Proyek Urutan, Segmen, Kumparan Jendela, Agregat Aliran) atau operator Agregat Jendela mode batch yang lebih baru dan biasanya lebih efisien.

Dalam kedua kasus, operator yang menangani perhitungan fungsi jendela perlu mencerna data yang dipesan oleh elemen partisi dan pengurutan jendela. Jika Anda tidak memiliki indeks pendukung, tentu saja SQL Server perlu memperkenalkan operator Sortir dalam rencana. Misalnya, jika Anda memiliki beberapa fungsi jendela dalam solusi Anda dengan lebih dari satu kombinasi unik dari partisi dan pemesanan, Anda pasti memiliki penyortiran eksplisit dalam rencana. Tetapi bagaimana jika Anda hanya memiliki satu kombinasi unik dari partisi dan pengurutan serta indeks pendukung?

Metode mode baris yang lebih lama dapat mengandalkan data yang telah dipesan sebelumnya yang diserap dari indeks tanpa memerlukan operator Sortir eksplisit dalam mode serial dan paralel. Operator mode batch yang lebih baru menghilangkan banyak inefisiensi dari pengoptimalan mode baris yang lebih lama dan memiliki manfaat yang melekat pada pemrosesan mode batch. Namun, implementasi paralelnya saat ini membutuhkan operator Sortir paralel mode batch perantara bahkan ketika ada indeks pendukung. Hanya implementasi serialnya yang dapat mengandalkan urutan indeks tanpa operator Sortir. Ini semua untuk mengatakan ketika pengoptimal perlu memilih strategi untuk menangani fungsi jendela Anda, dengan asumsi Anda memiliki indeks pendukung, biasanya akan menjadi salah satu dari empat opsi berikut:

  1. Mode baris, serial, tanpa penyortiran
  2. Mode baris, paralel, tanpa penyortiran
  3. Mode batch, serial, tanpa penyortiran
  4. Mode batch, paralel, penyortiran

Mana pun yang menghasilkan biaya paket terendah akan dipilih, dengan asumsi tentu saja prasyarat untuk paralelisme dan mode batch terpenuhi saat mempertimbangkannya. Biasanya, agar pengoptimal membenarkan rencana paralel, manfaat paralelisme harus lebih besar daripada pekerjaan ekstra seperti sinkronisasi utas. Dengan opsi 4 di atas, manfaat paralelisme harus lebih besar daripada pekerjaan ekstra biasa yang terkait dengan paralelisme, ditambah pengurutan ekstra.

Saat bereksperimen dengan berbagai solusi untuk tantangan kami, saya memiliki kasus di mana pengoptimal memilih opsi 2 di atas. Itu memilihnya meskipun fakta bahwa metode mode baris melibatkan beberapa inefisiensi karena manfaat paralelisme dan tidak ada penyortiran menghasilkan rencana dengan biaya lebih rendah daripada alternatif. Dalam beberapa kasus tersebut, memaksakan rencana serial dengan petunjuk menghasilkan opsi 3 di atas, dan dalam performa yang lebih baik.

Dengan mengingat latar belakang ini, mari kita lihat solusinya. Saya akan mulai dengan dua solusi yang mengandalkan teknik yang dikenal untuk tugas pulau yang tidak dapat lepas dari penyortiran eksplisit dalam rencana.

Solusi berdasarkan teknik yang diketahui 1

Berikut adalah solusi pertama untuk tantangan kami, yang didasarkan pada teknik yang telah dikenal selama ini:

DENGAN C AS( SELECT *, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY ord) - ROW_NUMBER() OVER(PARTITION BY grp, val ORDER BY ord) AS island FROM dbo.T1)SELECT grp, ord, val , ROW_NUMBER() OVER(PARTITION BY grp, val, island ORDER BY ord) AS seqnoFROM C;

Saya akan menyebutnya sebagai Solusi yang Diketahui 1.

CTE yang disebut C didasarkan pada kueri yang menghitung dua nomor baris. Yang pertama (saya akan menyebutnya sebagai rn1) dipartisi oleh grp dan diurutkan berdasarkan ord. Yang kedua (saya akan menyebutnya sebagai rn2) dipartisi oleh grp dan val dan diurutkan berdasarkan ord. Karena rn1 memiliki celah antara grup yang berbeda dari val yang sama dan rn2 tidak, perbedaan antara rn1 dan rn2 (kolom disebut pulau) adalah nilai konstanta unik untuk semua baris dengan nilai grp dan val yang sama. Berikut adalah hasil inner query, termasuk hasil perhitungan angka dua baris, yang tidak dikembalikan sebagai kolom terpisah:

grp ord val rn1 rn2 island-------- ----- ---- ---- ---- ------- Grup A 1002 Y 1 1 0Grup A 1003 Y 2 2 0Grup A 1005 Y 3 3 0Grup A 1007 N 4 1 3Grup A 1011 N 5 2 3Grup A 1013 N 6 3 3Grup A 1017 Y 7 4 3Grup A 1019 Y 8 5 3Grup A 1023 N 9 4 5Grup A 1029 N 10 5 5Grup B 1001 X 1 1 0Grup B 1002 X 2 2 0Grup B 1003 Z 3 1 2Grup B 1005 Z 4 2 2Grup B 1008 Z 5 3 2Grup B 1013 Z 6 4 2Grup B 1021 Y 7 1 6Grup B 1034 Y 8 2 6 

Yang tersisa untuk dilakukan kueri luar adalah menghitung kolom hasil seqno menggunakan fungsi ROW_NUMBER, dipartisi berdasarkan grp, val, dan island, dan diurutkan berdasarkan ord, menghasilkan hasil yang diinginkan.

Catatan Anda bisa mendapatkan nilai pulau yang sama untuk nilai nilai yang berbeda dalam partisi yang sama, seperti pada output di atas. Inilah mengapa penting untuk menggunakan grp, val, dan island sebagai elemen partisi jendela dan bukan grp dan island saja.

Demikian pula, jika Anda berurusan dengan tugas pulau yang mengharuskan Anda mengelompokkan data berdasarkan pulau dan menghitung agregat grup, Anda akan mengelompokkan baris berdasarkan grp, val, dan pulau. Tapi tidak demikian dengan tantangan kami. Di sini Anda hanya ditugaskan untuk menghitung nomor baris secara mandiri untuk setiap pulau.

Gambar 1 memiliki paket default yang saya dapatkan untuk solusi ini di mesin saya setelah mengisi tabel dengan 10 juta baris.

Gambar 1:Rencana paralel untuk Solusi yang Diketahui 1

Perhitungan rn1 dapat mengandalkan urutan indeks. Jadi, pengoptimal memilih strategi mode no sort + parallel row untuk yang satu ini (operator Proyek Segmen dan Urutan pertama dalam rencana). Karena perhitungan rn2 dan seqno menggunakan kombinasi unik mereka sendiri dari elemen partisi dan pengurutan, pengurutan eksplisit tidak dapat dihindari untuk mereka terlepas dari strategi yang digunakan. Jadi, pengoptimal memilih strategi mode sortir + batch paralel untuk keduanya. Paket ini melibatkan dua operator Sortir eksplisit.

Dalam pengujian kinerja saya, solusi ini membutuhkan waktu 3,68 detik untuk menyelesaikan 1 juta baris dan 43,1 detik melawan 10 juta baris.

Seperti yang disebutkan, saya menguji semua solusi juga dengan memaksa paket serial (dengan petunjuk MAXDOP 1). Rencana serial untuk solusi ini ditunjukkan pada Gambar 1.

Gambar 2:Rencana serial untuk Solusi yang Diketahui 1

Seperti yang diharapkan, kali ini juga perhitungan rn1 menggunakan strategi mode batch tanpa operator Sort sebelumnya, tetapi rencananya masih memiliki dua operator Sort untuk perhitungan nomor baris berikutnya. Paket serial berkinerja lebih buruk daripada yang paralel di mesin saya dengan semua ukuran input yang saya uji, membutuhkan 4,54 detik untuk menyelesaikan dengan 1 juta baris dan 61,5 detik dengan 10 juta baris.

Solusi berdasarkan teknik yang diketahui 2

Solusi kedua yang akan saya sajikan didasarkan pada teknik brilian untuk pendeteksian pulau yang saya pelajari dari Paul White beberapa tahun yang lalu. Berikut adalah kode solusi lengkap berdasarkan teknik ini:

DENGAN C1 AS( SELECT *, CASE WHEN val =LAG(val) OVER(PARTITION BY grp ORDER BY ord) THEN 0 ELSE 1 END AS isstart FROM dbo.T1),C2 AS( SELECT *, SUM(isstart) OVER(PARTITION BY grp ORDER BY ord ROWS UNBOUNDED PRECEDING) AS island FROM C1)SELECT grp, ord, val, ROW_NUMBER() OVER(PARTITION BY grp, island ORDER BY ord) AS seqnoFROM C2;

Saya akan menyebut solusi ini sebagai Solusi yang Diketahui 2.

Kueri yang mendefinisikan penghitungan CTE C1 menggunakan ekspresi CASE dan fungsi jendela LAG (dipartisi oleh grp dan diurutkan berdasarkan ord) untuk menghitung kolom hasil yang disebut isstart. Kolom ini memiliki nilai 0 ketika nilai val saat ini sama dengan sebelumnya dan 1 sebaliknya. Dengan kata lain, ini adalah 1 ketika baris adalah yang pertama di sebuah pulau dan 0 sebaliknya.

Berikut adalah output dari kueri yang mendefinisikan C1:

grp ord val isstart-------- ----- ---- --------Grup A 1002 Y 1Grup A 1003 Y 0Grup A 1005 Y 0Grup A 1007 N 1Grup A 1011 N 0Grup A 1013 N 0Grup A 1017 Y 1Grup A 1019 Y 0Grup A 1023 N 1Grup A 1029 N 0Grup B 1001 X 1Grup B 1002 X 0Grup B 1003 Z 1Grup B 1005 Z 0Grup B 1008 Z 0Grup B 1013 Z 0Grup B 1021 1Grup B 1034 Y 0

Keajaiban sejauh menyangkut deteksi pulau terjadi di CTE C2. Kueri yang mendefinisikannya menghitung pengidentifikasi pulau menggunakan fungsi jendela SUM (juga dipartisi oleh grp dan diurutkan berdasarkan ord) yang diterapkan ke kolom isstart. Kolom hasil dengan pengidentifikasi pulau disebut pulau. Dalam setiap partisi, Anda mendapatkan 1 untuk pulau pertama, 2 untuk pulau kedua, dan seterusnya. Jadi, kombinasi kolom grp dan pulau adalah pengidentifikasi pulau, yang dapat Anda gunakan dalam tugas pulau yang melibatkan pengelompokan berdasarkan pulau jika relevan.

Berikut adalah output dari kueri yang mendefinisikan C2:

grp ord val isstart island-------- ----- ---- -------- -------Grup A 1002 Y 1 1Grup A 1003 Y 0 1Grup A 1005 Y 0 1Grup A 1007 N 1 2Grup A 1011 N 0 2Grup A 1013 N 0 2Grup A 1017 Y 1 3Grup A 1019 Y 0 3Grup A 1023 N 1 4Grup A 1029 N 0 4Grup B 1001 X 1 1Grup B 1002 X 0 1Grup B 1003 Z 1 2Grup B 1005 Z 0 2Grup B 1008 Z 0 2Grup B 1013 Z 0 2Grup B 1021 Y 1 3Grup B 1034 Y 0 3

Terakhir, kueri luar menghitung seqno kolom hasil yang diinginkan dengan fungsi ROW_NUMBER, dipartisi oleh grp dan pulau, dan diurutkan berdasarkan ord. Perhatikan kombinasi elemen partisi dan pengurutan ini berbeda dari yang digunakan oleh fungsi jendela sebelumnya. Sedangkan perhitungan dua fungsi jendela pertama berpotensi mengandalkan urutan indeks, yang terakhir tidak bisa.

Gambar 3 memiliki paket default yang saya dapatkan untuk solusi ini.

Gambar 3:Rencana paralel untuk Solusi yang Diketahui 2

Seperti yang Anda lihat dalam rencana, perhitungan dua fungsi jendela pertama menggunakan strategi mode no sort + parallel row, dan komputasi terakhir menggunakan strategi sort + mode batch paralel.

Waktu berjalan yang saya dapatkan untuk solusi ini berkisar dari 2,57 detik untuk 1 juta baris hingga 46,2 detik untuk 10 juta baris.

Saat memaksa pemrosesan serial, saya mendapatkan rencana yang ditunjukkan pada Gambar 4.

Gambar 4:Rencana serial untuk Solusi yang Diketahui 2

Seperti yang diharapkan, kali ini semua perhitungan fungsi jendela bergantung pada strategi mode batch. Dua yang pertama tanpa penyortiran sebelumnya, dan yang terakhir dengan. Paket paralel dan paket serial melibatkan satu operator Sortir eksplisit. Paket serial berkinerja lebih baik daripada paket paralel di mesin saya dengan ukuran input yang saya uji. Waktu berjalan yang saya dapatkan untuk paket serial paksa berkisar dari 1,75 detik untuk 1 juta baris hingga 21,7 detik untuk 10 juta baris.

Solusi berdasarkan teknik baru

Ketika Erland memperkenalkan tantangan ini di forum pribadi, orang-orang skeptis terhadap kemungkinan menyelesaikannya dengan kueri yang telah dioptimalkan dengan rencana tanpa penyortiran eksplisit. Itu saja yang perlu saya dengar untuk memotivasi saya. Jadi, inilah yang saya dapatkan:

DENGAN C1 AS( SELECT *, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY ord) AS rn, LAG(val) OVER(PARTITION BY grp ORDER BY ord) AS prev FROM dbo.T1),C2 AS( SELECT *, MAX(CASE WHEN val =prev THEN NULL ELSE rn END) OVER(PARTITION BY grp ORDER BY ord ROWS UNBOUNDED PRECEDING) AS firstrn FROM C1)SELECT grp, ord, val, rn - firstrn + 1 AS seqnoFROM C2; 

Solusinya menggunakan tiga fungsi jendela:LAG, ROW_NUMBER dan MAX. Yang penting di sini adalah ketiganya didasarkan pada partisi grp dan urutan urutan, yang selaras dengan urutan kunci indeks pendukung.

Kueri yang mendefinisikan CTE C1 menggunakan fungsi ROW_NUMBER untuk menghitung nomor baris (kolom rn), dan fungsi LAG untuk mengembalikan nilai nilai sebelumnya (kolom sebelumnya).

Berikut adalah output dari kueri yang mendefinisikan C1:

grp ord val rn prev-------- ----- ---- ---- --- -----Grup A 1002 Y 1 NULLGroup A 1003 Y 2 YGroup A 1005 Y 3 YGroup A 1007 N 4 Grup A 1011 N 5 Grup A 1013 N 6 Grup A 1017 Y 7 Grup A 1019 Y 8 Grup A 1023 N 9 Grup A 1029 N 10 Grup B 1001 X 1 NULL Grup B 1002 X 2 X Grup B 1003 Z 3 X Grup B 1005 Z 4 ZGroup B 1008 Z 5 ZGroup B 1013 Z 6 ZGroup B 1021 Y 7 ZGroup B 1034 Y 8 Y

Perhatikan ketika val dan prev sama, itu bukan baris pertama di pulau, sebaliknya.

Berdasarkan logika ini, kueri yang mendefinisikan CTE C2 menggunakan ekspresi CASE yang mengembalikan rn ketika baris adalah yang pertama di sebuah pulau dan NULL sebaliknya. Kode kemudian menerapkan fungsi jendela MAX ke hasil ekspresi CASE, mengembalikan rn pertama pulau (kolom pertama).

Berikut adalah output dari kueri yang mendefinisikan C2, termasuk output dari ekspresi CASE:

grp ord val rn prev KASUS pertama-------- ----- ---- --- ----- ----- -------- Grup A 1002 Y 1 NULL 1 1Grup A 1003 Y 2 Y NULL 1Grup A 1005 Y 3 Y NULL 1Grup A 1007 N 4 Y 4 4Grup A 1011 N 5 N NULL 4Grup A 1013 N 6 N NULL 4Grup A 1017 Y 7 N 7 7Grup A 1019 Y 8 Y NULL 7Grup A 1023 N 9 Y 9 9Grup A 1029 N 10 N NULL 9Grup B 1001 X 1 NULL 1 1Grup B 1002 X 2 X NULL 1Grup B 1003 Z 3 X 3 3Grup B 1005 Z 4 Z NULL 3Grup B 1008 Z 5 Z NULL 3Grup B 1013 Z 6 Z NULL 3Grup B 1021 Y 7 Z 7 7Grup B 1034 Y 8 Y NULL 7

Yang tersisa untuk kueri luar adalah menghitung kolom hasil yang diinginkan seqno sebagai rn dikurangi firstrn ditambah 1.

Gambar 5 memiliki rencana paralel default yang saya dapatkan untuk solusi ini saat mengujinya menggunakan pernyataan SELECT INTO, menulis hasilnya ke dalam tabel sementara.

Gambar 5:Rencana paralel untuk solusi baru

Tidak ada operator sortir eksplisit dalam rencana ini. Namun, ketiga fungsi jendela dihitung menggunakan strategi mode no sort + parallel row, jadi kami kehilangan manfaat pemrosesan batch. Waktu berjalan yang saya dapatkan untuk solusi ini dengan paket paralel berkisar antara 2,47 detik terhadap 1 juta baris dan 41,4 terhadap 10 juta baris.

Di sini, ada kemungkinan yang cukup tinggi untuk paket serial dengan pemrosesan batch untuk bekerja lebih baik secara signifikan, terutama ketika mesin tidak memiliki banyak CPU. Ingat saya sedang menguji solusi saya terhadap mesin dengan 4 CPU logis. Gambar 6 memiliki rencana yang saya dapatkan untuk solusi ini saat memaksa pemrosesan serial.

Gambar 6:Rencana serial untuk solusi baru

Ketiga fungsi jendela menggunakan strategi mode batch no sort + serial. Dan hasilnya cukup mengesankan. Waktu berjalan solusi ini berkisar dari 0,5 detik terhadap 1 juta baris dan 5,49 detik terhadap 10 juta baris. Yang juga membuat penasaran tentang solusi ini adalah ketika mengujinya sebagai pernyataan SELECT normal (dengan hasil yang dibuang) sebagai lawan dari pernyataan SELECT INTO, SQL Server memilih paket serial secara default. Dengan dua solusi lainnya, saya mendapatkan paket paralel secara default baik dengan SELECT dan dengan SELECT INTO.

Lihat bagian selanjutnya untuk mengetahui hasil tes kinerja lengkap.

Perbandingan kinerja

Berikut kode yang saya gunakan untuk menguji tiga solusi, tentu saja menghapus komentar pada petunjuk MAXDOP 1 untuk menguji paket serial:

-- Uji Solusi yang Diketahui 1 DROP TABLE JIKA ADA #Result; DENGAN C AS( SELECT *, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY ord) - ROW_NUMBER() OVER(PARTITION BY grp, val ORDER BY ord) AS island FROM dbo.T1)SELECT grp, ord, val, ROW_NUMBER( ) OVER(PARTITION BY grp, val, island ORDER BY ord) AS seqnoINTO #ResultFROM C/* OPTION(MAXDOP 1) */; -- batalkan komentar untuk serial testGO -- Uji Solusi yang Diketahui 2 DROP TABLE JIKA ADA #Hasil; DENGAN C1 AS( SELECT *, CASE WHEN val =LAG(val) OVER(PARTITION BY grp ORDER BY ord) THEN 0 ELSE 1 END AS isstart FROM dbo.T1),C2 AS( SELECT *, SUM(isstart) OVER(PARTITION OLEH grp ORDER BY ord ROWS UNBOUNDED PRECEDING) AS island FROM C1)SELECT grp, ord, val, ROW_NUMBER() OVER(PARTITION BY grp, island ORDER BY ord) AS seqnoINTO #ResultFROM C2/* OPTION(MAXDOP 1) */; GO -- Uji Solusi Baru DROP TABLE JIKA ADA #Result; DENGAN C1 AS( SELECT *, ROW_NUMBER() OVER(PARTITION BY grp ORDER BY ord) AS rn, LAG(val) OVER(PARTITION BY grp ORDER BY ord) AS prev FROM dbo.T1),C2 AS( SELECT *, MAX (CASE WHEN val =prev THEN NULL ELSE rn END) OVER(PARTITION BY grp ORDER BY ord ROWS UNBOUNDED PRECEDING) AS firstrn FROM C1)SELECT grp, ord, val, rn - firstrn + 1 AS seqnoINTO #ResultFROM C2/* OPTION( MAXDOP 1) */;

Gambar 7 memiliki waktu berjalan dari paket paralel dan serial untuk semua solusi terhadap ukuran input yang berbeda.

Gambar 7:Perbandingan kinerja

Solusi baru, menggunakan mode serial, adalah pemenang yang jelas. Ini memiliki kinerja hebat, penskalaan linier, dan waktu respons langsung.

Kesimpulan

Tugas pulau cukup umum dalam kehidupan nyata. Banyak dari mereka melibatkan baik mengidentifikasi pulau dan mengelompokkan data berdasarkan pulau. Tantangan pulau-pulau Erland yang menjadi fokus artikel ini sedikit lebih unik karena tidak melibatkan pengelompokan melainkan pengurutan setiap baris pulau dengan nomor baris.

Saya menyajikan dua solusi berdasarkan teknik yang dikenal untuk mengidentifikasi pulau. Masalah dengan keduanya adalah mereka menghasilkan rencana yang melibatkan penyortiran eksplisit, yang berdampak negatif pada kinerja, skalabilitas, dan waktu respons dari solusi. Saya juga mempresentasikan teknik baru yang menghasilkan rencana tanpa penyortiran sama sekali. Paket serial untuk solusi ini, yang menggunakan strategi mode batch no sort + serial, memiliki kinerja yang sangat baik, penskalaan linier, dan waktu respons langsung. Sangat disayangkan, setidaknya untuk saat ini, kami tidak dapat memiliki strategi mode batch paralel tanpa sortir untuk menangani fungsi jendela.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL ATAU Operator untuk Pemula

  2. Tabel Ubah SQL

  3. Memigrasikan Cluster Cassandra Anda

  4. Cara Menghitung Nilai Berbeda dalam SQL

  5. Melakukan Audit Perubahan Data Menggunakan Tabel Temporal