Wenn Sie die Daten nicht SPEICHERN müssen (was Sie nicht tun sollten, da Sie die laufenden Summen jedes Mal aktualisieren müssen, wenn eine Zeile geändert, hinzugefügt oder gelöscht wird) und wenn Sie dem skurrilen Update (dem Sie sollte nicht, da es nicht garantiert funktioniert und sein Verhalten sich mit einem Hotfix, Service Pack, Upgrade oder sogar einer zugrunde liegenden Index- oder Statistikänderung ändern könnte), können Sie diese Art von Abfrage zur Laufzeit ausprobieren. Dies ist eine Methode, die MVP-Kollege Hugo Kornelis als „satzbasierte Iteration“ bezeichnete (er hat etwas Ähnliches in einem seiner Kapitel von gepostet). SQL Server MVP Deep Dives ). Da laufende Summen normalerweise einen Cursor über dem gesamten Satz, eine skurrile Aktualisierung über den gesamten Satz oder einen einzelnen nichtlinearen Self-Join erfordern, der mit zunehmender Zeilenanzahl immer teurer wird, besteht der Trick hier darin, einige endliche zu durchlaufen Element in der Menge (in diesem Fall der "Rang" jeder Zeile in Bezug auf den Monat für jeden Benutzer - und Sie verarbeiten jeden Rang nur einmal für alle Benutzer/Monat-Kombinationen auf diesem Rang, also anstatt 200.000 Zeilen zu durchlaufen, Sie Schleifen bis zu 24 Mal).
DECLARE @t TABLE
(
[user_id] INT,
[month] TINYINT,
total DECIMAL(10,1),
RunningTotal DECIMAL(10,1),
Rnk INT
);
INSERT @t SELECT [user_id], [month], total, total,
RANK() OVER (PARTITION BY [user_id] ORDER BY [month])
FROM dbo.my_table;
DECLARE @rnk INT = 1, @rc INT = 1;
WHILE @rc > 0
BEGIN
SET @rnk += 1;
UPDATE c SET RunningTotal = p.RunningTotal + c.total
FROM @t AS c INNER JOIN @t AS p
ON c.[user_id] = p.[user_id]
AND p.rnk = @rnk - 1
AND c.rnk = @rnk;
SET @rc = @@ROWCOUNT;
END
SELECT [user_id], [month], total, RunningTotal
FROM @t
ORDER BY [user_id], rnk;
Ergebnisse:
user_id month total RunningTotal
------- ----- ----- ------------
1 1 2.0 2.0
1 2 1.0 3.0
1 3 3.5 6.5 -- I think your calculation is off
2 1 0.5 0.5
2 2 1.5 2.0
2 3 2.0 4.0
Natürlich können Sie Aktualisieren Sie die Basistabelle aus dieser Tabellenvariablen, aber warum sollten Sie sich die Mühe machen, da diese gespeicherten Werte nur gültig sind, bis die Tabelle das nächste Mal von einer DML-Anweisung berührt wird?
UPDATE mt
SET cumulative_total = t.RunningTotal
FROM dbo.my_table AS mt
INNER JOIN @t AS t
ON mt.[user_id] = t.[user_id]
AND mt.[month] = t.[month];
Da wir uns nicht auf irgendeine implizite Reihenfolge verlassen, wird dies zu 100 % unterstützt und verdient einen Leistungsvergleich mit dem nicht unterstützten schrulligen Update. Auch wenn es nicht schlägt, aber nahe kommt, sollten Sie IMHO trotzdem in Betracht ziehen, es zu verwenden.
Bezüglich der SQL Server 2012-Lösung erwähnt Matt RANGE
Da diese Methode jedoch einen Spool auf der Festplatte verwendet, sollten Sie auch mit ROWS
testen anstatt einfach mit RANGE
zu laufen . Hier ist ein kurzes Beispiel für Ihren Fall:
SELECT
[user_id],
[month],
total,
RunningTotal = SUM(total) OVER
(
PARTITION BY [user_id]
ORDER BY [month] ROWS UNBOUNDED PRECEDING
)
FROM dbo.my_table
ORDER BY [user_id], [month];
Vergleichen Sie dies mit RANGE UNBOUNDED PRECEDING
oder kein ROWS\RANGE
überhaupt (was auch den RANGE
verwendet). Spool auf der Festplatte). Die oben genannten haben eine geringere Gesamtdauer und Weg weniger I/O, obwohl der Plan etwas komplexer aussieht (ein zusätzlicher Sequenz-Projektoperator).
Ich habe kürzlich einen Blogpost veröffentlicht, in dem einige Leistungsunterschiede beschrieben werden, die ich für ein bestimmtes Szenario mit laufenden Summen beobachtet habe:
http://www.sqlperformance.com/2012/07 /t-sql-queries/laufende-summen