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:
SELECT somedate, somevalue,
SUM(somevalue) OVER(ORDER BY somedate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM Table
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:
drop table #t
create table #t ( ord int primary key, total int, running_total int)
insert #t(ord,total) values (2,20)
-- notice the malicious re-ordering
insert #t(ord,total) values (1,10)
insert #t(ord,total) values (3,10)
insert #t(ord,total) values (4,1)
declare @total int
set @total = 0
update #t set running_total = @total, @total = @total + total
select * from #t
order by ord
ord total running_total
----------- ----------- -------------
1 10 10
2 20 30
3 10 40
4 1 41
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:
create table #t ( ord int primary key, total int, running_total int)
set nocount on
declare @i int
set @i = 0
begin tran
while @i < 10000
begin
insert #t (ord, total) values (@i, rand() * 100)
set @i = @i +1
end
commit
Tes 1:
SELECT ord,total,
(SELECT SUM(total)
FROM #t b
WHERE b.ord <= a.ord) AS b
FROM #t a
-- CPU 11731, Reads 154934, Duration 11135
Tes 2:
SELECT a.ord, a.total, SUM(b.total) AS RunningTotal
FROM #t a CROSS JOIN #t b
WHERE (b.ord <= a.ord)
GROUP BY a.ord,a.total
ORDER BY a.ord
-- CPU 16053, Reads 154935, Duration 4647
Tes 3:
DECLARE @TotalTable table(ord int primary key, total int, running_total int)
DECLARE forward_cursor CURSOR FAST_FORWARD
FOR
SELECT ord, total
FROM #t
ORDER BY ord
OPEN forward_cursor
DECLARE @running_total int,
@ord int,
@total int
SET @running_total = 0
FETCH NEXT FROM forward_cursor INTO @ord, @total
WHILE (@@FETCH_STATUS = 0)
BEGIN
SET @running_total = @running_total + @total
INSERT @TotalTable VALUES(@ord, @total, @running_total)
FETCH NEXT FROM forward_cursor INTO @ord, @total
END
CLOSE forward_cursor
DEALLOCATE forward_cursor
SELECT * FROM @TotalTable
-- CPU 359, Reads 30392, Duration 496
Tes 4:
declare @total int
set @total = 0
update #t set running_total = @total, @total = @total + total
select * from #t
-- CPU 0, Reads 58, Duration 139