Aktualisieren Sie , wenn Sie SQL Server 2012 ausführen, folgende Informationen : https://stackoverflow.com/a/10309947
Das Problem ist, dass die SQL Server-Implementierung der Over-Klausel etwas eingeschränkt ist .
Mit Oracle (und ANSI-SQL) können Sie Folgendes tun:
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 Bauch sagt mir, dass dies einer der seltenen Fälle ist, in denen ein Cursor am schnellsten ist, obwohl ich ein Benchmarking für große Ergebnisse durchführen muss.
Der Update-Trick ist praktisch, aber ich finde ihn ziemlich zerbrechlich. Wenn Sie eine vollständige Tabelle aktualisieren, wird diese anscheinend in der Reihenfolge des Primärschlüssels ausgeführt. Wenn Sie also Ihr Datum als aufsteigenden Primärschlüssel festlegen, werden Sie dies tunprobably
sicher. Sie verlassen sich jedoch auf ein undokumentiertes SQL Server-Implementierungsdetail (auch wenn die Abfrage von zwei Prozessen ausgeführt wird, frage ich mich, was passieren wird, siehe: MAXDOP):
Vollständiges Arbeitsprobe:
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, dies ist der Tiefpunkt.
Der schnellste sichere Weg, dies zu tun, wäre der Cursor. Er ist eine Größenordnung schneller als die korrelierte Unterabfrage von Cross-Join.
Der absolut schnellste Weg ist der UPDATE-Trick. Ich mache mir nur Sorgen, dass ich nicht sicher bin, ob das Update unter allen Umständen linear verläuft. Die Abfrage enthält nichts, was dies ausdrücklich sagt.
Unterm Strich würde ich für den 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