Aggiornamento , se si esegue SQL Server 2012, consultare: https://stackoverflow.com/a/10309947
Il problema è che l'implementazione di SQL Server della clausola Over è piuttosto limitata .
Oracle (e ANSI-SQL) ti consentono di fare cose come:
SELECT somedate, somevalue,
SUM(somevalue) OVER(ORDER BY somedate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM Table
SQL Server non offre una soluzione pulita a questo problema. Il mio istinto mi sta dicendo che questo è uno di quei rari casi in cui un cursore è il più veloce, anche se dovrò fare dei benchmark su grandi risultati.
Il trucco di aggiornamento è utile ma mi sembra abbastanza fragile. Sembra che se stai aggiornando una tabella completa, procederà nell'ordine della chiave primaria. Quindi, se imposti la data come chiave primaria crescente, probably
sarai al sicuro. Ma stai facendo affidamento su un dettaglio dell'implementazione di SQL Server non documentato (anche se la query finisce per essere eseguita da due proc mi chiedo cosa accadrà, vedi: MAXDOP):
Campione di lavoro completo:
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
Hai chiesto un benchmark questo è il basso.
Il modo più veloce e sicuro per farlo sarebbe il Cursore, è un ordine di grandezza più veloce della sottoquery correlata del cross-join.
Il modo più veloce in assoluto è il trucco UPDATE. La mia unica preoccupazione è che non sono sicuro che in ogni caso l'aggiornamento procederà in modo lineare. Non c'è nulla nella query che lo dice esplicitamente.
In conclusione, per il codice di produzione andrei con il cursore.
Dati di test:
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
Test 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
Test 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
Test 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
Test 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