web-dev-qa-db-de.com

So erhalten Sie eine kumulierte Summe

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 das Folgende?

id  srome   CumSrome
1   10  10
2   12  22
3   3   25
4   15  40
5   23  63
143
ps.
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 |

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

181
RedFilter

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 führt in 34 Sekunden über 5 Millionen Zeilen für mich aus.

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

152
Gareth Adamson

Für SQL Server 2012 kann es einfach sein:

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

weil ORDER BY-Klausel für SUM standardmäßig RANGE UNBOUNDED PRECEDING AND CURRENT ROW für Fensterrahmen bedeutet ("Allgemeine Bemerkungen" unter https://msdn.Microsoft.com/en-us/library/ms189461.aspx )

17

Lass zuerst eine Tabelle mit Dummy-Daten erstellen ->

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 füge ich dieselbe Tabelle hinzu (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 nur das Somevalue von t2 zusammen und wir bekommen die ans  

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 besser)

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ösche die Dummy-Tabelle

12

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
10
Damir Sudarevic

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

Die Generierung von kumulativen Summen kann mit der CROSS APPLY-Logik weiter optimiert werden.

Funktioniert besser als INNER JOIN & OVER Clause, wenn 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
6
Aditya

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

3
Ritesh Khatri

In diesem ausgezeichneten Beitrag ist eine viel schnellere CTE-Implementierung verfügbar: http://weblogs.sqlteam.com/mladenp/archive/2009/07/28/SQL-Server-2005-Fast-Running-Totals.aspx

Das Problem in diesem Thread kann folgendermaßen ausgedrückt werden:

    DECLARE @RT INT
    SELECT @RT = 0

    ;
    WITH  abcd
            AS ( SELECT TOP 100 percent
                        id
                       ,SomeNumt
                       ,MySum
                       order by id
               )
      update abcd
      set @RT = MySum = @RT + SomeNumt
      output inserted.*
 </ code>
2
cezarm

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
1
Julian

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
1
Porsh

Die SQL-Lösung, die "ROWS ZWISCHEN UNBEGRENZTEN PRECEDING UND CURRENT ROW" und "SUM" kombiniert, hat genau das gemacht, was ich erreichen wollte .... Vielen Dank!

Wenn es jemandem helfen kann, war hier mein Fall. Ich wollte +1 in einer Spalte kumulieren, wenn ein Hersteller als "Some Maker" (Beispiel) gefunden wird. Wenn nicht, kein Inkrement, sondern vorheriges 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, so dass wir nicht +1 geben, sondern stattdessen die vertikale Anzahl von Some Maker bis zur nächsten Zeile auf 2 gehalten wird .. Die Partitionierung erfolgt nach Benutzer.

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 derselben Situation befindet. Ich habe versucht, SUMME und PARTITION zu kombinieren, aber die erstaunliche Syntax "ROWS BETWEEN UNBUNDED PRECEDING AND CURRENT ROW" vervollständigte die Aufgabe. 

Danke! Groaker

0
Groaker

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
0
Sachin

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;
0
bellonid

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

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
0