Saya mencoba untuk menghitung total berjalan. Tetapi harus me-reset ketika jumlah kumulatif lebih besar dari nilai kolom lain
create table #reset_runn_total
(
id int identity(1,1),
val int,
reset_val int,
grp int
)
insert into #reset_runn_total
values
(1,10,1),
(8,12,1),(6,14,1),(5,10,1),(6,13,1),(3,11,1),(9,8,1),(10,12,1)
SELECT Row_number()OVER(partition BY grp ORDER BY id)AS rn,*
INTO #test
FROM #reset_runn_total
Detail indeks:
CREATE UNIQUE CLUSTERED INDEX ix_load_reset_runn_total
ON #test(rn, grp)
contoh data
+----+-----+-----------+-----+
| id | val | reset_val | Grp |
+----+-----+-----------+-----+
| 1 | 1 | 10 | 1 |
| 2 | 8 | 12 | 1 |
| 3 | 6 | 14 | 1 |
| 4 | 5 | 10 | 1 |
| 5 | 6 | 13 | 1 |
| 6 | 3 | 11 | 1 |
| 7 | 9 | 8 | 1 |
| 8 | 10 | 12 | 1 |
+----+-----+-----------+-----+
Hasil yang diharapkan
+----+-----+-----------------+-------------+
| id | val | reset_val | Running_tot |
+----+-----+-----------------+-------------+
| 1 | 1 | 10 | 1 |
| 2 | 8 | 12 | 9 | --1+8
| 3 | 6 | 14 | 15 | --1+8+6 -- greater than reset val
| 4 | 5 | 10 | 5 | --reset
| 5 | 6 | 13 | 11 | --5+6
| 6 | 3 | 11 | 14 | --5+6+3 -- greater than reset val
| 7 | 9 | 8 | 9 | --reset -- greater than reset val
| 8 | 10 | 12 | 10 | --reset
+----+-----+-----------------+-------------+
Pertanyaan:
Saya mendapat hasilnya menggunakan Recursive CTE
. Pertanyaan asli ada di sini /programming/42085404/reset-running-total-based-on-another-column
;WITH cte
AS (SELECT rn,id,
val,
reset_val,
grp,
val AS running_total,
Iif (val > reset_val, 1, 0) AS flag
FROM #test
WHERE rn = 1
UNION ALL
SELECT r.*,
Iif(c.flag = 1, r.val, c.running_total + r.val),
Iif(Iif(c.flag = 1, r.val, c.running_total + r.val) > r.reset_val, 1, 0)
FROM cte c
JOIN #test r
ON r.grp = c.grp
AND r.rn = c.rn + 1)
SELECT *
FROM cte
Apakah ada alternatif yang lebih baik T-SQL
tanpa menggunakan CLR
.?
50000
kelompok dengan60
Id . jadi jumlah total rekaman akan ada3000000
. YakinRecursive CTE
tidak akan skala pada3000000
. Akan memperbarui metrik ketika saya kembali ke kantor. Bisakah kita mencapai ini menggunakansum()Over(Order by)
seperti yang Anda gunakan dalam artikel ini sqlperformance.com/2012/07/t-sql-queries/running-totalsJawaban:
Saya telah melihat masalah yang sama dan tidak pernah dapat menemukan solusi fungsi jendela yang melakukan satu kali melewati data. Saya pikir itu tidak mungkin. Fungsi jendela harus dapat diterapkan ke semua nilai dalam kolom. Itu membuat perhitungan reset seperti ini sangat sulit, karena satu reset mengubah nilai untuk semua nilai berikut.
Salah satu cara untuk memikirkan masalah ini adalah Anda bisa mendapatkan hasil akhir yang Anda inginkan jika Anda menghitung total running dasar selama Anda bisa mengurangi total running dari baris sebelumnya yang benar. Misalnya, dalam data sampel Anda nilai untuk
id
4 adalahrunning total of row 4 - the running total of row 3
. Nilai untukid
6 adalahrunning total of row 6 - the running total of row 3
karena reset belum terjadi. Nilai untukid
7 adalahrunning total of row 7 - the running total of row 6
dan seterusnya.Saya akan mendekati ini dengan T-SQL dalam satu lingkaran. Saya sedikit terbawa suasana dan berpikir saya punya solusi lengkap. Selama 3 juta baris dan 500 grup kode selesai dalam 24 detik di desktop saya. Saya menguji dengan edisi Pengembang SQL Server 2016 dengan 6 vCPU. Saya mengambil keuntungan dari sisipan paralel dan eksekusi paralel secara umum sehingga Anda mungkin perlu mengubah kode jika Anda menggunakan versi yang lebih lama atau memiliki batasan DOP.
Di bawah kode yang saya gunakan untuk menghasilkan data. Rentang pada
VAL
danRESET_VAL
harus serupa dengan data sampel Anda.Algoritma adalah sebagai berikut:
1) Mulailah dengan memasukkan semua baris dengan total running standar ke tabel temp.
2) Dalam satu lingkaran:
2a) Untuk setiap grup, hitung baris pertama dengan total running di atas reset_value yang tersisa di tabel dan simpan id, total running yang terlalu besar, dan total running sebelumnya yang terlalu besar di tabel temp.
2b) Hapus baris dari tabel temp pertama ke tabel temp hasil yang memiliki
ID
kurang dari atau sama denganID
di tabel temp kedua. Gunakan kolom lain untuk menyesuaikan total berjalan sesuai kebutuhan.3) Setelah penghapusan, tidak ada lagi proses, baris menjalankan tambahan
DELETE OUTPUT
ke tabel hasil. Ini untuk baris di akhir grup yang tidak pernah melebihi nilai reset.Saya akan melalui satu implementasi algoritma di atas dalam langkah-demi-langkah T-SQL.
Mulai dengan membuat beberapa tabel temp.
#initial_results
memegang data asli dengan total running standar,#group_bookkeeping
diperbarui setiap loop untuk mengetahui baris mana yang dapat dipindahkan, dan#final_results
berisi hasilnya dengan total running yang disesuaikan untuk reset.Saya membuat indeks berkerumun di tabel temp setelah begitu memasukkan dan membangun indeks dapat dilakukan secara paralel. Membuat perbedaan besar pada mesin saya tetapi mungkin tidak pada mesin Anda. Membuat indeks pada tabel sumber sepertinya tidak membantu tetapi itu bisa membantu pada mesin Anda.
Kode di bawah ini berjalan dalam loop dan memperbarui tabel pembukuan. Untuk setiap grup, kita perlu mendapatkan hasil maksimum
ID
yang harus dipindahkan ke tabel hasil. Kita membutuhkan total running dari baris itu sehingga kita bisa mengurangkannya dari total running awal. Thegrp_done
kolom diatur ke 1 ketika tidak ada lagi pekerjaan yang harus dilakukan untukgrp
.Benar-benar bukan penggemar
LOOP JOIN
petunjuk secara umum, tetapi ini adalah permintaan sederhana dan itu adalah cara tercepat untuk mendapatkan apa yang saya inginkan. Untuk benar-benar mengoptimalkan waktu respons, saya ingin bergabung loop paralel bersarang alih-alih bergabung menggabungkan DOP 1.Kode di bawah ini berjalan dalam loop dan memindahkan data dari tabel awal ke tabel hasil akhir. Perhatikan penyesuaian terhadap total running awal.
Untuk kenyamanan Anda, di bawah ini adalah kode lengkap:
sumber
Recursive CTE
butuh 2 menit dan 15 detikMenggunakan CURSOR:
Periksa di sini: http://rextester.com/WSPLO95303
sumber
Tidak berjendela, tetapi versi SQL murni:
Saya bukan spesialis dalam dialek SQL Server. Ini adalah versi awal untuk PostrgreSQL (jika saya mengerti benar saya tidak bisa menggunakan LIMIT 1 / TOP 1 di bagian rekursif dalam SQL Server):
sumber
grp
kolom.Tampaknya Anda memiliki beberapa pertanyaan / metode untuk menyerang masalah tetapi Anda belum memberikan kami - atau bahkan mempertimbangkan? - indeks di atas meja.
Indeks apa yang ada di tabel? Apakah itu tumpukan atau apakah itu memiliki indeks berkerumun?
Saya akan mencoba berbagai solusi yang disarankan setelah menambahkan indeks ini:
Atau cukup ubah (atau buat) indeks yang dikelompokkan menjadi
(grp, id)
.Memiliki indeks yang menargetkan permintaan spesifik harus meningkatkan efisiensi - sebagian besar atau semua metode.
sumber