Bayangkan tabel berikut (disebut TestTable
):
id somedate somevalue
-- -------- ---------
45 01/Jan/09 3
23 08/Jan/09 5
12 02/Feb/09 0
77 14/Feb/09 7
39 20/Feb/09 34
33 02/Mar/09 6
Saya ingin kueri yang mengembalikan total berjalan dalam urutan tanggal, seperti:
id somedate somevalue runningtotal
-- -------- --------- ------------
45 01/Jan/09 3 3
23 08/Jan/09 5 8
12 02/Feb/09 0 8
77 14/Feb/09 7 15
39 20/Feb/09 34 49
33 02/Mar/09 6 55
Saya tahu ada berbagai cara untuk melakukan ini di SQL Server 2000/2005/2008.
Saya sangat tertarik dengan metode semacam ini yang menggunakan trik pernyataan-agregat-set-pernyataan:
INSERT INTO @AnotherTbl(id, somedate, somevalue, runningtotal)
SELECT id, somedate, somevalue, null
FROM TestTable
ORDER BY somedate
DECLARE @RunningTotal int
SET @RunningTotal = 0
UPDATE @AnotherTbl
SET @RunningTotal = runningtotal = @RunningTotal + somevalue
FROM @AnotherTbl
... ini sangat efisien tetapi saya telah mendengar ada masalah di sekitar ini karena Anda tidak dapat selalu menjamin bahwa UPDATE
pernyataan akan memproses baris dalam urutan yang benar. Mungkin kita bisa mendapatkan jawaban yang pasti tentang masalah itu.
Tapi mungkin ada cara lain yang bisa disarankan orang?
sunting: Sekarang dengan SqlFiddle dengan setup dan contoh 'trik pembaruan' di atas
sql
sql-server
tsql
running-total
seperti kode
sumber
sumber
Jawaban:
Pembaruan , jika Anda menjalankan SQL Server 2012 lihat: https://stackoverflow.com/a/10309947
Masalahnya adalah bahwa implementasi SQL Server dari klausa Over agak terbatas .
Oracle (dan ANSI-SQL) memungkinkan Anda melakukan hal-hal seperti:
SQL Server tidak memberi Anda solusi bersih untuk masalah ini. Perasaan saya mengatakan kepada saya bahwa ini adalah salah satu kasus yang jarang terjadi di mana kursor adalah yang tercepat, meskipun saya harus melakukan pembandingan pada hasil yang besar.
Trik pembaruan berguna tetapi saya merasa ini cukup rapuh. Tampaknya jika Anda memperbarui tabel lengkap maka itu akan melanjutkan dalam urutan kunci utama. Jadi, jika Anda menetapkan tanggal sebagai kunci primer naik, Anda akan
probably
aman. Tetapi Anda mengandalkan detail implementasi SQL Server tidak berdokumen (juga jika kueri akhirnya dilakukan oleh dua procs. Saya ingin tahu apa yang akan terjadi, lihat: MAXDOP):Sampel kerja penuh:
Anda meminta patokan, ini adalah lowdown.
Cara AMAN tercepat untuk melakukan ini adalah kursor, ini adalah urutan besarnya lebih cepat dari sub-kueri berkorelasi dengan cross-join.
Cara tercepat mutlak adalah trik UPDATE. Satu-satunya kekhawatiran saya adalah bahwa saya tidak yakin bahwa dalam semua keadaan pembaruan akan diproses secara linear. Tidak ada dalam kueri yang secara eksplisit mengatakan demikian.
Intinya, untuk kode produksi saya akan pergi dengan kursor.
Data uji:
Tes 1:
Tes 2:
Tes 3:
Tes 4:
sumber
Di SQL Server 2012 Anda bisa menggunakan SUM () dengan klausa OVER () .
SQL Fiddle
sumber
Sementara Sam Saffron melakukan pekerjaan besar, ia masih tidak menyediakan kode ekspresi tabel rekursif umum untuk masalah ini. Dan bagi kami yang bekerja dengan SQL Server 2008 R2 dan bukan Denali, ini masih cara tercepat untuk menjalankan total, ini sekitar 10 kali lebih cepat daripada kursor di komputer kerja saya untuk 100000 baris, dan juga inline query.
Jadi, ini dia (saya kira ada
ord
kolom di tabel dan nomor berurutan tanpa celah, untuk pemrosesan cepat juga harus ada batasan unik pada nomor ini):sql fiddle demo
pembaruan Saya juga ingin tahu tentang pembaruan ini dengan pembaruan variabel atau unik . Jadi biasanya itu berfungsi dengan baik, tetapi bagaimana kita bisa yakin itu bekerja setiap saat? baik, inilah sedikit trik (temukan di sini - http://www.sqlservercentral.com/Forums/Topic802558-203-21.aspx#bm981258 ) - Anda cukup memeriksa tugas saat ini dan sebelumnya
ord
dan menggunakan1/0
jika ada perbedaan dari apa Anda mengharapkan:Dari apa yang saya lihat jika Anda memiliki indeks / kunci utama berkerumun yang tepat di meja Anda (dalam kasus kami akan diindeks oleh
ord_id
) pembaruan akan diproses secara linear sepanjang waktu (tidak pernah dijumpai dengan angka nol). Yang mengatakan, itu terserah Anda untuk memutuskan apakah Anda ingin menggunakannya dalam kode produksi :)pembaruan 2 Saya menautkan jawaban ini, karena ini mencakup beberapa info bermanfaat tentang tidak dapat diandalkannya pembaruan unik - nvarchar concatenation / index / nvarchar (max) perilaku yang tidak dapat dijelaskan .
sumber
Operator BERLAKU di SQL 2005 dan yang lebih tinggi berfungsi untuk ini:
sumber
Anda juga dapat menggunakan fungsi ROW_NUMBER () dan tabel temp untuk membuat kolom arbitrer untuk digunakan dalam perbandingan pada pernyataan SELECT bagian dalam.
sumber
Gunakan sub-kueri yang berkorelasi. Sangat sederhana, ini dia:
Kode mungkin tidak tepat benar, tapi saya yakin idenya benar.
GROUP BY jika seandainya tanggal muncul lebih dari sekali, Anda hanya ingin melihatnya sekali di set hasil.
Jika Anda tidak keberatan melihat tanggal berulang, atau Anda ingin melihat nilai dan id asli, maka berikut ini yang Anda inginkan:
sumber
Anda juga dapat melakukan denormalkan - menyimpan total yang berjalan di tabel yang sama:
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx
Memilih bekerja jauh lebih cepat daripada solusi lain, tetapi modifikasi mungkin lebih lambat
sumber
Dengan asumsi bahwa windowing berfungsi pada SQL Server 2008 seperti yang dilakukannya di tempat lain (yang telah saya coba), cobalah:
MSDN mengatakan itu tersedia di SQL Server 2008 (dan mungkin 2005 juga?) Tapi saya tidak punya contoh untuk mencobanya.
EDIT: well, rupanya SQL Server tidak mengizinkan spesifikasi jendela ("LEBIH (...)") tanpa menentukan "PARTITION BY" (membagi hasilnya menjadi beberapa kelompok tetapi tidak menggabungkan dengan cara yang dilakukan GROUP BY). Mengganggu - referensi sintaks MSDN menunjukkan bahwa itu opsional, tetapi saya hanya memiliki SqlServer 2000 contoh sekitar saat ini.
Kueri yang saya berikan bekerja di Oracle 10.2.0.3.0 dan PostgreSQL 8.4-beta. Jadi beritahu MS untuk mengejar ketinggalan;)
sumber
1 partitionme
dan partisi dengan itu. Selain itu, partisi oleh mungkin diperlukan dalam situasi kehidupan nyata ketika melakukan laporan.Jika Anda menggunakan Sql server 2008 R2 di atas. Maka, Ini akan menjadi cara terpendek untuk dilakukan;
LAG digunakan untuk mendapatkan nilai baris sebelumnya. Anda dapat melakukan google untuk info lebih lanjut.
[1]:
sumber
SUM(somevalue) OVER(...)
yang tampaknya jauh lebih bersih bagi sayaSaya percaya total yang berjalan dapat dicapai dengan menggunakan operasi INNER JOIN sederhana di bawah ini.
sumber
Berikut ini akan menghasilkan hasil yang diperlukan.
Memiliki indeks berkerumun di SomeDate akan sangat meningkatkan kinerja.
sumber
Menggunakan gabung Variasi lain adalah menggunakan gabung. Sekarang kueri bisa terlihat seperti:
untuk lebih lanjut Anda dapat mengunjungi tautan ini http://askme.indianyouth.info/details/calculating-simple-running-totals-in-sql-server-12
sumber
Meskipun cara terbaik untuk menyelesaikannya adalah menggunakan fungsi jendela, itu juga dapat dilakukan dengan menggunakan sub-kueri berkorelasi sederhana .
sumber
sumber
Berikut adalah 2 cara sederhana untuk menghitung jumlah running:
Pendekatan 1 : Dapat ditulis dengan cara ini jika DBMS Anda mendukung Fungsi Analitik
Pendekatan 2 : Anda dapat menggunakan OUTER APPLY jika versi database / DBMS Anda sendiri tidak mendukung Fungsi Analitik
Catatan: - Jika Anda harus menghitung total running untuk partisi berbeda secara terpisah, itu dapat dilakukan seperti yang diposting di sini: Menghitung total running di seluruh baris dan pengelompokan berdasarkan ID
sumber