Aktualisieren , wenn Sie SQL Server 2012 ausführen, siehe:https://stackoverflow.com/a/10309947
Das Problem besteht darin, dass die SQL Server-Implementierung der Over-Klausel etwas eingeschränkt ist.
Oracle (und ANSI-SQL) erlauben Ihnen Dinge wie:
SELECT somedate, somevalue,
SUM(somevalue) OVER(ORDER BY somedate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM Table
SQL Server bietet Ihnen keine saubere Lösung für dieses Problem. Mein Bauchgefühl sagt mir, dass dies einer der seltenen Fälle ist, in denen ein Cursor am schnellsten ist, obwohl ich bei großen Ergebnissen ein Benchmarking durchführen muss.
Der Update-Trick ist praktisch, aber ich fühle mich ziemlich zerbrechlich. Es scheint, dass, wenn Sie eine vollständige Tabelle aktualisieren, diese in der Reihenfolge des Primärschlüssels fortfahren wird. Wenn Sie also Ihr Datum als Primärschlüssel aufsteigend setzen, werden Sie probably
sicher sein. Aber Sie verlassen sich auf ein undokumentiertes SQL Server-Implementierungsdetail (auch wenn die Abfrage am Ende von zwei Procs ausgeführt wird, frage ich mich, was passieren wird, siehe:MAXDOP):
Voll funktionsfähiges Beispiel:
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
Sie haben nach einem Benchmark gefragt, das sind die Fakten.
Der schnellste und SICHERste Weg, dies zu tun, wäre der Cursor, er ist um eine Größenordnung schneller als die korrelierte Unterabfrage von Cross-Join.
Der absolut schnellste Weg ist der UPDATE-Trick. Meine einzige Sorge dabei ist, dass ich nicht sicher bin, ob das Update unter allen Umständen linear ablaufen wird. Es gibt nichts in der Abfrage, die dies ausdrücklich sagt.
Unterm Strich würde ich für Produktionscode mit dem Cursor gehen.
Testdaten:
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