Stellen Sie sich die folgende Tabelle vor (aufgerufen TestTable
):
id somedate somevalue
-- -------- ---------
45 01/Jan/09 3
23 08/Jan/09 5
12 02/Feb/09 0
77 14/Feb/09 7
39 20/Feb/09 34
33 02/Mar/09 6
Ich möchte eine Abfrage, die eine laufende Summe in Datumsreihenfolge zurückgibt, wie:
id somedate somevalue runningtotal
-- -------- --------- ------------
45 01/Jan/09 3 3
23 08/Jan/09 5 8
12 02/Feb/09 0 8
77 14/Feb/09 7 15
39 20/Feb/09 34 49
33 02/Mar/09 6 55
Ich weiß, dass es in SQL Server 2000/2005/2008 verschiedene Möglichkeiten gibt, dies zu tun .
Ich interessiere mich besonders für diese Art von Methode, die den Trick Aggregating-Set-Statement verwendet:
INSERT INTO @AnotherTbl(id, somedate, somevalue, runningtotal)
SELECT id, somedate, somevalue, null
FROM TestTable
ORDER BY somedate
DECLARE @RunningTotal int
SET @RunningTotal = 0
UPDATE @AnotherTbl
SET @RunningTotal = runningtotal = @RunningTotal + somevalue
FROM @AnotherTbl
... das ist sehr effizient, aber ich habe gehört, dass es Probleme gibt, weil Sie nicht unbedingt garantieren können, dass die UPDATE
Anweisung die Zeilen in der richtigen Reihenfolge verarbeitet. Vielleicht können wir einige endgültige Antworten zu diesem Thema bekommen.
Aber vielleicht gibt es andere Möglichkeiten, die die Leute vorschlagen können?
edit: Jetzt mit einer SqlFiddle mit dem Setup und dem 'Update Trick' Beispiel oben
quelle
Antworten:
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:
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 tun
probably
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:
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:
Test 1:
Test 2:
Test 3:
Test 4:
quelle
In SQL Server 2012 können Sie SUM () mit der OVER () -Klausel verwenden.
SQL Fiddle
quelle
Während Sam Saffron großartige Arbeit daran geleistet hat, hat er für dieses Problem immer noch keinen rekursiven allgemeinen Tabellenausdruckcode bereitgestellt. Und für uns, die mit SQL Server 2008 R2 und nicht mit Denali arbeiten, ist dies immer noch der schnellste Weg, um insgesamt ausgeführt zu werden. Es ist ungefähr zehnmal schneller als der Cursor auf meinem Arbeitscomputer für 100000 Zeilen und es ist auch eine Inline-Abfrage.
Also, hier ist es (ich nehme an, dass es eine
ord
Spalte in der Tabelle gibt und die fortlaufende Nummer ohne Lücken, für eine schnelle Verarbeitung sollte es auch eine eindeutige Einschränkung für diese Nummer geben):sql fiddle demo
Update Ich war auch neugierig auf dieses Update mit variablem oder skurrilem Update . Normalerweise funktioniert es also in Ordnung, aber wie können wir sicher sein, dass es jedes Mal funktioniert? Nun, hier ist ein kleiner Trick (hier zu finden - http://www.sqlservercentral.com/Forums/Topic802558-203-21.aspx#bm981258 ) - Sie überprüfen einfach die aktuelle und vorherige
ord
und verwenden die1/0
Zuordnung, falls sie sich von der unterscheidet Sie erwarten:Nach dem, was ich gesehen habe, wenn Sie einen richtigen Clustered-Index / Primärschlüssel in Ihrer Tabelle haben (in unserem Fall wäre es ein Index nach
ord_id
), wird die Aktualisierung die ganze Zeit linear durchgeführt (nie angetroffen durch Null teilen). Das heißt, es liegt an Ihnen zu entscheiden, ob Sie es im Produktionscode verwenden möchten :)Update 2 Ich verknüpfe diese Antwort, da sie einige nützliche Informationen zur Unzuverlässigkeit des skurrilen Updates enthält - nvarchar Verkettung / Index / nvarchar (max) unerklärliches Verhalten .
quelle
Der APPLY-Operator in SQL 2005 und höher funktioniert dafür:
quelle
Sie können auch die Funktion ROW_NUMBER () und eine temporäre Tabelle verwenden, um eine beliebige Spalte zu erstellen, die für den Vergleich der inneren SELECT-Anweisung verwendet werden soll.
quelle
Verwenden Sie eine korrelierte Unterabfrage. Sehr einfach, los geht's:
Der Code ist möglicherweise nicht genau korrekt, aber ich bin mir sicher, dass die Idee so ist.
Wenn ein Datum mehrmals angezeigt wird, möchten Sie es in der Ergebnismenge nur einmal sehen.
Wenn es Ihnen nichts ausmacht, sich wiederholende Daten zu sehen, oder wenn Sie den ursprünglichen Wert und die ID sehen möchten, möchten Sie Folgendes:
quelle
Sie können auch laufende Summen denormalisieren - in derselben Tabelle speichern:
http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/23/denormalizing-to-enforce-business-rules-running-totals.aspx
Ausgewählte arbeiten viel schneller als alle anderen Lösungen, aber Änderungen können langsamer sein
quelle
Angenommen, die Fensterung funktioniert unter SQL Server 2008 wie anderswo (was ich versucht habe), probieren Sie Folgendes aus:
MSDN sagt, dass es in SQL Server 2008 (und vielleicht auch 2005?) Verfügbar ist, aber ich habe keine Instanz zur Hand, um es zu versuchen.
BEARBEITEN: Nun, anscheinend erlaubt SQL Server keine Fensterspezifikation ("OVER (...)") ohne Angabe von "PARTITION BY" (Aufteilung des Ergebnisses in Gruppen, aber nicht so aggregieren wie GROUP BY). Ärgerlich - die MSDN-Syntaxreferenz legt nahe, dass dies optional ist, aber ich habe derzeit nur SqlServer 2000-Instanzen.
Die Abfrage, die ich gestellt habe, funktioniert sowohl in Oracle 10.2.0.3.0 als auch in PostgreSQL 8.4-beta. Also sag MS, sie soll aufholen;)
quelle
1 partitionme
und partitionieren Sie danach. Außerdem ist in realen Situationen bei der Erstellung von Berichten wahrscheinlich eine Partitionierung nach erforderlich.Wenn Sie den oben genannten SQL Server 2008 R2 verwenden. Dann wäre es der kürzeste Weg;
LAG wird verwendet, um den vorherigen Zeilenwert abzurufen. Sie können Google für weitere Informationen tun.
[1]:
quelle
SUM(somevalue) OVER(...)
was mir viel sauberer erscheintIch glaube, dass mit der folgenden einfachen INNER JOIN-Operation eine laufende Summe erreicht werden kann.
quelle
Im Folgenden werden die erforderlichen Ergebnisse erzielt.
Ein Clustered-Index für SomeDate verbessert die Leistung erheblich.
quelle
Join verwenden Eine andere Variante ist die Verwendung von Join. Jetzt könnte die Abfrage folgendermaßen aussehen:
Weitere Informationen finden Sie unter http://askme.indianyouth.info/details/calculating-simple-running-totals-in-sql-server-12
quelle
Der beste Weg, dies zu erreichen, ist die Verwendung einer Fensterfunktion. Sie kann jedoch auch eine einfache korrelierte Unterabfrage verwenden .
quelle
quelle
Hier sind 2 einfache Methoden zur Berechnung der laufenden Summe:
Ansatz 1 : Es kann so geschrieben werden, wenn Ihr DBMS analytische Funktionen unterstützt
Ansatz 2 : Sie können OUTER APPLY verwenden, wenn Ihre Datenbankversion / DBMS selbst keine Analysefunktionen unterstützt
Hinweis: - Wenn Sie die laufende Summe für verschiedene Partitionen separat berechnen müssen, können Sie wie hier beschrieben vorgehen: Berechnung der laufenden Summe über Zeilen hinweg und Gruppierung nach ID
quelle