So erhalten Sie eine kumulative Summe

185
declare  @t table
    (
        id int,
        SomeNumt int
    )

insert into @t
select 1,10
union
select 2,12
union
select 3,3
union
select 4,15
union
select 5,23


select * from @t

Die obige Auswahl gibt mir Folgendes zurück.

id  SomeNumt
1   10
2   12
3   3
4   15
5   23

Wie bekomme ich folgendes:

id  srome   CumSrome
1   10  10
2   12  22
3   3   25
4   15  40
5   23  63
ps.
quelle
5
Es ist nicht schwer, laufende Summen in T-SQL zu erhalten. Es gibt viele richtige Antworten, die meisten davon sind ziemlich einfach. Was derzeit nicht einfach (oder sogar möglich) ist, ist das Schreiben einer echten Abfrage in T-SQL, um Summen effizient auszuführen. Sie sind alle O (n ^ 2), obwohl sie leicht O (n) sein könnten, außer dass T-SQL für diesen Fall nicht optimiert. Sie können O (n) mit Cursors und / oder While-Schleifen erhalten, aber dann verwenden Sie Cursors. ( blech! )
RBarryYoung

Antworten:

225
select t1.id, t1.SomeNumt, SUM(t2.SomeNumt) as sum
from @t t1
inner join @t t2 on t1.id >= t2.id
group by t1.id, t1.SomeNumt
order by t1.id

SQL Fiddle Beispiel

Ausgabe

| ID | SOMENUMT | SUM |
-----------------------
|  1 |       10 |  10 |
|  2 |       12 |  22 |
|  3 |        3 |  25 |
|  4 |       15 |  40 |
|  5 |       23 |  63 |

Bearbeiten: Dies ist eine allgemeine Lösung, die auf den meisten Datenbankplattformen funktioniert. Wenn es eine bessere Lösung für Ihre spezifische Plattform gibt (z. B. Gareths), verwenden Sie sie!

RedFilter
quelle
12
@Franklin Nur kostengünstig für kleine Tische. Die Kosten steigen proportional zum Quadrat der Anzahl der Zeilen. Mit SQL Server 2012 kann dies viel effizienter durchgeführt werden.
Martin Smith
3
FWIW, ich habe mir dabei von einem DBA die Knöchel schlagen lassen. Ich denke, der Grund dafür ist, dass es sehr teuer wird, sehr schnell.
Davon abgesehen
@BenDundee Einverstanden - Ich neige dazu, allgemeine SQL-Lösungen bereitzustellen, die auf den meisten Datenbankplattformen funktionieren. Wie immer, wenn es einen besseren Ansatz gibt, z. B. Gareths, verwenden Sie ihn!
RedFilter
198

Die neueste Version von SQL Server (2012) ermöglicht Folgendes.

SELECT 
    RowID, 
    Col1,
    SUM(Col1) OVER(ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId

oder

SELECT 
    GroupID, 
    RowID, 
    Col1,
    SUM(Col1) OVER(PARTITION BY GroupID ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId

Das geht noch schneller. Die partitionierte Version ist für mich in 34 Sekunden über 5 Millionen Zeilen abgeschlossen.

Vielen Dank an Peso, der den SQL-Team-Thread kommentiert hat, auf den in einer anderen Antwort verwiesen wird.

Gareth Adamson
quelle
22
Der Kürze halber können Sie ROWS UNBOUNDED PRECEDINGanstelle von verwenden ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
Dan
1
Hinweis: Wenn die Spalte, die Sie kumulativ summieren möchten, selbst bereits eine Summe oder eine Zählung ist, können Sie das Ganze entweder als innere Abfrage einschließen oder tatsächlich ausführen SUM(COUNT(*)) OVER (ORDER BY RowId ROWS UNBOUNDED PRECEDING) AS CumulativeSum. Es war mir nicht sofort klar, ob es funktionieren würde, aber es tat es :-)
Simon_Weaver
Verfügbar in PostgreSQL ab 8.4: postgresql.org/docs/8.4/sql-select.html
ADJenks
26

Ab SQL Server 2012 kann dies einfach sein:

SELECT id, SomeNumt, sum(SomeNumt) OVER (ORDER BY id) as CumSrome FROM @t

weil ORDER BYKlausel für SUMstandardmäßig RANGE UNBOUNDED PRECEDING AND CURRENT ROWfür Fensterrahmen bedeutet ("Allgemeine Bemerkungen" unter https://msdn.microsoft.com/en-us/library/ms189461.aspx )

Andrew Karakotov
quelle
13

Eine CTE-Version, nur zum Spaß:

;
WITH  abcd
        AS ( SELECT id
                   ,SomeNumt
                   ,SomeNumt AS MySum
             FROM   @t
             WHERE  id = 1
             UNION ALL
             SELECT t.id
                   ,t.SomeNumt
                   ,t.SomeNumt + a.MySum AS MySum
             FROM   @t AS t
                    JOIN abcd AS a ON a.id = t.id - 1
           )
  SELECT  *  FROM    abcd
OPTION  ( MAXRECURSION 1000 ) -- limit recursion here, or 0 for no limit.

Kehrt zurück:

id          SomeNumt    MySum
----------- ----------- -----------
1           10          10
2           12          22
3           3           25
4           15          40
5           23          63
Damir Sudarevic
quelle
12

Erstellen wir zunächst eine Tabelle mit Dummy-Daten ->

Create Table CUMULATIVESUM (id tinyint , SomeValue tinyint)

**Now let put some data in the table**

Insert Into CUMULATIVESUM

Select 1, 10 union 
Select 2, 2  union
Select 3, 6  union
Select 4, 10 

hier trete ich demselben Tisch bei (SELF Joining)

Select c1.ID, c1.SomeValue, c2.SomeValue
From CumulativeSum c1,  CumulativeSum c2
Where c1.id >= c2.ID
Order By c1.id Asc

ERGEBNIS:

ID  SomeValue   SomeValue
1   10          10
2   2           10
2   2            2
3   6           10
3   6            2
3   6            6
4   10          10
4   10           2
4   10           6
4   10          10

hier gehen wir jetzt einfach den Somevalue von t2 summieren und wir werden die ans bekommen

Select c1.ID, c1.SomeValue, Sum(c2.SomeValue) CumulativeSumValue
From CumulativeSum c1,  CumulativeSum c2
Where c1.id >= c2.ID
Group By c1.ID, c1.SomeValue
Order By c1.id Asc

FÜR SQL SERVER 2012 und höher (viel bessere Leistung)

Select c1.ID, c1.SomeValue, 
SUM (SomeValue) OVER (ORDER BY c1.ID )
From CumulativeSum c1
Order By c1.id Asc

Erwünschtes Ergebnis

ID  SomeValue   CumlativeSumValue
1   10          10
2   2           12
3   6           18
4   10          28

Drop Table CumulativeSum

Löschen Sie den Dummy-Tisch

Neeraj Prasad Sharma
quelle
Bitte bearbeiten Sie Ihre Antwort und formatieren Sie den Code, um ihn lesbar zu machen
Kleopatra
Was ist, wenn mi "ID" -Werte wiederholt werden? (Sie sind offensichtlich kein Primärschlüssel in meiner Tabelle.) Ich konnte diese Abfrage nicht an diesen Fall anpassen.
Pablete
AFAIK Sie benötigen eine eindeutige ID für die kumulative Summe und können diese mit row_number abrufen. Überprüfen Sie den folgenden Code :; mit NewTBLWITHUNiqueID als (wählen Sie row_number () über (Reihenfolge nach ID, Wert) UniqueID, * From CUMULATIVESUMwithoutPK)
Neeraj Prasad Sharma
Danke @NeerajPrasadSharma, ich habe tatsächlich eine rank()andere Order-by-Klausel verwendet, um es zu lösen.
Pablete
5

Späte Antwort, zeigt aber noch eine Möglichkeit ...

Die kumulative Summengenerierung kann mit der CROSS APPLYLogik optimiert werden.

Funktioniert besser als das INNER JOIN&, OVER Clausewenn der tatsächliche Abfrageplan analysiert wird ...

/* Create table & populate data */
IF OBJECT_ID('tempdb..#TMP') IS NOT NULL
DROP TABLE #TMP 

SELECT * INTO #TMP 
FROM (
SELECT 1 AS id
UNION 
SELECT 2 AS id
UNION 
SELECT 3 AS id
UNION 
SELECT 4 AS id
UNION 
SELECT 5 AS id
) Tab


/* Using CROSS APPLY 
Query cost relative to the batch 17%
*/    
SELECT   T1.id, 
         T2.CumSum 
FROM     #TMP T1 
         CROSS APPLY ( 
         SELECT   SUM(T2.id) AS CumSum 
         FROM     #TMP T2 
         WHERE    T1.id >= T2.id
         ) T2

/* Using INNER JOIN 
Query cost relative to the batch 46%
*/
SELECT   T1.id, 
         SUM(T2.id) CumSum
FROM     #TMP T1
         INNER JOIN #TMP T2
                 ON T1.id > = T2.id
GROUP BY T1.id

/* Using OVER clause
Query cost relative to the batch 37%
*/
SELECT   T1.id, 
         SUM(T1.id) OVER( PARTITION BY id)
FROM     #TMP T1

Output:-
  id       CumSum
-------   ------- 
   1         1
   2         3
   3         6
   4         10
   5         15
Aditya
quelle
1
Ich bin nicht überzeugt. "Abfragekosten im Verhältnis zum Stapel" ist für den Vergleich der Leistung von Abfragen bedeutungslos. Abfragekosten sind Schätzungen, die vom Abfrageplaner verwendet werden, um verschiedene Pläne schnell abzuwägen und die kostengünstigsten auszuwählen. Diese Kosten dienen jedoch dem Vergleich von Plänen für dieselbe Abfrage und sind zwischen Abfragen überhaupt nicht relevant oder vergleichbar . Dieser Beispieldatensatz ist auch zu klein, um einen signifikanten Unterschied zwischen den drei Methoden zu erkennen. Versuchen Sie es erneut mit 1 m Zeilen, sehen Sie sich die tatsächlichen Ausführungspläne an, versuchen Sie es mit set io statistics onund vergleichen Sie die CPU und die tatsächlichen Zeiten.
Davos
4

Select *, (Select SUM(SOMENUMT) From @t S Where S.id <= M.id) From @t M

Ritesh Khatri
quelle
Dies ist eine sehr clevere Methode, um das Ergebnis zu erzielen, und Sie können der Summe mehrere Bedingungen hinzufügen.
RaRdEvA
@RaRdEvA Es ist jedoch nicht besonders gut für die Leistung, es wird ausgeführt, dass correlated subqueryfür jede einzelne Zeile der Ergebnismenge immer mehr Zeilen gescannt werden . Es wird keine laufende Summe beibehalten und die Daten werden einmal wie bei Fensterfunktionen gescannt.
Davos
1
@Davos Sie haben Recht, wenn Sie es verwenden, wird es über 100.000 Datensätze sehr langsam.
RaRdEvA
1

Sobald die Tabelle erstellt ist -

select 
    A.id, A.SomeNumt, SUM(B.SomeNumt) as sum
    from @t A, @t B where A.id >= B.id
    group by A.id, A.SomeNumt

order by A.id
Porsh
quelle
1

Oben (Pre-SQL12) sehen wir Beispiele wie diese: -

SELECT
    T1.id, SUM(T2.id) AS CumSum
FROM 
    #TMP T1
    JOIN #TMP T2 ON T2.id < = T1.id
GROUP BY
    T1.id

Effizienter...

SELECT
    T1.id, SUM(T2.id) + T1.id AS CumSum
FROM 
    #TMP T1
    JOIN #TMP T2 ON T2.id < T1.id
GROUP BY
    T1.id
julianisch
quelle
1

Sie können diese einfache Abfrage für die progressive Berechnung verwenden:

select 
   id
  ,SomeNumt
  ,sum(SomeNumt) over(order by id ROWS between UNBOUNDED PRECEDING and CURRENT ROW) as CumSrome
from @t
Afif Pratama
quelle
0

Versuche dies

select 
    t.id,
    t.SomeNumt, 
    sum(t.SomeNumt) Over (Order by t.id asc Rows Between Unbounded Preceding and Current Row) as cum
from 
    @t t 
group by
    t.id,
    t.SomeNumt
order by
    t.id asc;
bellonid
quelle
Dies funktioniert mit SQL Server 2012 und höher. 2008 werden Fensterfunktionen nur eingeschränkt unterstützt.
Peter Smit
0

Versuche dies:

CREATE TABLE #t(
 [name] varchar NULL,
 [val] [int] NULL,
 [ID] [int] NULL
) ON [PRIMARY]

insert into #t (id,name,val) values
 (1,'A',10), (2,'B',20), (3,'C',30)

select t1.id, t1.val, SUM(t2.val) as cumSum
 from #t t1 inner join #t t2 on t1.id >= t2.id
 group by t1.id, t1.val order by t1.id
Sachin
quelle
0

Die SQL-Lösung, die "ROWS ZWISCHEN UNBOUNDED PRECEDING AND CURRENT ROW" und "SUM" kombiniert, hat genau das getan, was ich erreichen wollte. Ich danke dir sehr!

Wenn es jemandem helfen kann, war hier mein Fall. Ich wollte +1 in einer Spalte kumulieren, wenn ein Hersteller als "Some Maker" gefunden wird (Beispiel). Wenn nicht, kein Inkrement, sondern das vorherige Inkrementergebnis anzeigen.

Also dieses Stück SQL:

SUM( CASE [rmaker] WHEN 'Some Maker' THEN  1 ELSE 0 END) 
OVER 
(PARTITION BY UserID ORDER BY UserID,[rrank] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Cumul_CNT

Erlaubte mir so etwas zu bekommen:

User 1  Rank1   MakerA      0  
User 1  Rank2   MakerB      0  
User 1  Rank3   Some Maker  1  
User 1  Rank4   Some Maker  2  
User 1  Rank5   MakerC      2
User 1  Rank6   Some Maker  3  
User 2  Rank1   MakerA      0  
User 2  Rank2   SomeMaker   1  

Erklärung von oben: Es beginnt die Zählung von "Some Maker" mit 0, Some Maker wird gefunden und wir machen +1. Für Benutzer 1 wird MakerC gefunden, sodass wir nicht +1 ausführen, sondern die vertikale Anzahl von Some Maker bis zur nächsten Zeile auf 2 bleibt. Die Partitionierung erfolgt nach Benutzer. Wenn wir also den Benutzer wechseln, ist die kumulative Anzahl wieder auf Null.

Ich bin auf der Arbeit, ich möchte keinen Verdienst für diese Antwort, sagen Sie einfach Danke und zeigen Sie mein Beispiel, falls sich jemand in der gleichen Situation befindet. Ich habe versucht, SUM und PARTITION zu kombinieren, aber die erstaunliche Syntax "ROWS ZWISCHEN UNBOUNDED PRECEDING UND CURRENT ROW" hat die Aufgabe abgeschlossen.

Vielen Dank! Groaker

Groaker
quelle
0

Ohne Verwendung eines kumulativen JOIN-Gehalts für eine Person, die mithilfe der folgenden Abfrage abgerufen wird:

SELECT * , (
  SELECT SUM( salary ) 
  FROM  `abc` AS table1
  WHERE table1.ID <=  `abc`.ID
    AND table1.name =  `abc`.Name
) AS cum
FROM  `abc` 
ORDER BY Name
Rudar Daman Singla
quelle
0

Zum Beispiel: WENN Sie eine Tabelle mit zwei Spalten haben, ist eine ID und die zweite Nummer und möchte die kumulative Summe herausfinden.

SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
JP
quelle