Kümülatif toplamı alma


186
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

yukarıdaki seçim bana aşağıdakileri döndürür.

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

Aşağıdakileri nasıl alabilirim:

id  srome   CumSrome
1   10  10
2   12  22
3   3   25
4   15  40
5   23  63

5
T-SQL'de toplamları çalıştırmak zor değil, birçok doğru cevap var, çoğu oldukça kolay. Kolay olmayan (ya da şu anda mümkün olan), verimli çalışan toplamlar için T-SQL'de gerçek bir sorgu yazmaktır. Hepsi O (n ^ 2), ancak kolayca O (n) olabilirler, ancak T-SQL bu durum için optimize etmez. O (n) imleçleri ve / veya While döngülerini kullanarak alabilirsiniz, ancak daha sonra İmleçler kullanıyorsunuz. ( blech! )
RBarryYoung

Yanıtlar:


226
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 örneği

Çıktı

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

Düzenleme: Bu, çoğu db platformunda çalışacak genelleştirilmiş bir çözümdür. Özel platformunuz için daha iyi bir çözüm olduğunda (örneğin, gareth's), onu kullanın!


12
@Franklin Sadece küçük masalar için uygun maliyetli. Maliyet, satır sayısının karesiyle orantılı olarak artar. SQL Server 2012, bunun çok daha verimli bir şekilde yapılmasını sağlar.
Martin Smith

3
FWIW, bunu bir DBA tarafından yaparken boğduğumu vurdum. Bence bunun sebebi çok pahalı ve hızlı olması. Çoğu veri analisti / bilim insanının bu sorunu bir veya iki kez çözmek zorunda kalması gerektiği için bu harika bir röportaj sorusudur :)
BenDundee

@BenDundee Kabul - Çoğu db platformlarında çalışacak genelleştirilmiş SQL çözümleri sağlama eğilimindedir. Her zaman olduğu gibi, örneğin gareths gibi daha iyi bir yaklaşım olduğunda bunu kullanın!
RedFilter

199

SQL Server'ın (2012) en son sürümü aşağıdakilere izin verir.

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

veya

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

Bu daha da hızlı. Bölümlenmiş sürüm benim için 5 milyondan fazla satırda 34 saniye içinde tamamlanıyor.

Başka bir cevapta atıfta bulunulan SQL Team iş parçacığını yorumlayan Peso sayesinde.


22
Kısacası, ROWS UNBOUNDED PRECEDINGyerine kullanabilirsiniz ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
Dan

1
Not: Kümülatif olarak toplamak istediğiniz sütunun kendisi zaten bir toplam veya sayıysa, her şeyi bir iç sorgu olarak sarabilir veya gerçekten yapabilirsiniz SUM(COUNT(*)) OVER (ORDER BY RowId ROWS UNBOUNDED PRECEDING) AS CumulativeSum. Çalışırsa hemen bana belli değildi ama işe yaradı :-)
Simon_Weaver

PostgreSQL'de 8.4 itibariyle mevcut: postgresql.org/docs/8.4/sql-select.html
ADJenks


13

Sadece eğlenmek için bir CTE sürümü:

;
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.

İadeler:

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

13

İlk önce kukla verilerle bir tablo oluşturalım ->

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 

burada aynı masaya katılıyorum (SELF Katılıyor)

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

SONUÇ:

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

işte şimdi t2 Somevalue'yu özetliyoruz ve ans'ı alacağız

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

SQL SERVER 2012 ve üstü (Çok daha iyi performans)

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

İstenen sonuç

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

Drop Table CumulativeSum

Kuklaları temizle


lütfen cevabınızı düzenleyin ve okunabilir hale getirmek için kodu formatlayın
kleopatra

Mi "ID" değerleri tekrarlanırsa ne olur? (onlar obvoiusly benim tablonun birincil anahtar değil) Ben bu sorguyu bu duruma adapte edemedim?
pablete

AFAIK birikimli toplam için benzersiz bir kimliğe ihtiyacınız var ve bunu row_number kullanarak alabilirsiniz. Aşağıdaki kodu kontrol edin: NewTBLWITHUNiqueID ile (satır_sayısı () üzerinden (kimliğe göre sipariş, somevalue) UniqueID, * CUMULATIVESUMwithoutPK'dan)
Neeraj Prasad Sharma

Teşekkürler @NeerajPrasadSharma, aslında rank()ve bunu çözmek için fıkra tarafından başka bir sipariş kullandım .
Pablete

5

Geç cevap ama bir ihtimal daha var ...

Kümülatif Toplam üretimi CROSS APPLYmantık ile daha iyi hale getirilebilir .

Daha iyi çalışır INNER JOINve OVER Clausene zaman gerçek sorgu planını analiz ...

/* 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

1
Ben ikna olmadım. "Yığına göre sorgu maliyeti", sorguların performansını karşılaştırmak için anlamsız bir şeydir. Sorgu maliyetleri, sorgu planlayıcısı tarafından farklı planları hızlı bir şekilde tartmak ve en az maliyetli olanı seçmek için kullanılan tahminlerdir, ancak bu maliyetler aynı sorgu için planları karşılaştırmak içindir ve sorgular arasında hiç alakalı değil veya karşılaştırılabilir değildir. Bu örnek veri kümesi, üç yöntem arasında önemli bir fark göremeyecek kadar küçüktür. 1m satırlarla tekrar deneyin, gerçek yürütme planlarına bakın, deneyin set io statistics onve cpu ile gerçek zamanları karşılaştırın.
Davos

4

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


Sonucu elde etmenin çok akıllı bir yoludur ve toplama birden fazla koşul ekleyebilirsiniz.
RaRdEvA

@RaRdEvA Performans için harika değil correlated subquery, sonuç kümesinin her bir satırı için çalışır ve gittikçe daha fazla satır tarar. Çalışan bir toplam tutmaz ve pencere işlevleri gibi verileri bir kez taramaz.
Davos

1
@Davos haklı, eğer kullanırsanız 100.000'den fazla kayıt çok yavaşlıyor.
RaRdEvA


2

Aşamalı hesaplama için bu basit sorguyu kullanabilirsiniz:

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

1

Tablo oluşturulduktan sonra -

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

Yukarıda (Pre-SQL12) bunun gibi örnekler görüyoruz:

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

Daha verimli...

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

0

Bunu dene

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;

Bu SQL Server 2012 ve üstü ile çalışır, 2008 pencere fonksiyonları için sınırlı desteğe sahiptir.
Peter Smit

0

Bunu dene:

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

"SINIRSIZ ÖNCELEME VE AKIM SATIRI ARASINDAKİ SIRALAR" ve "TOPLA" tam olarak istediğim şeyi yaptı. Çok teşekkür ederim!

Eğer herkese yardım edebilirse, işte benim durumumdu. Bir yapıcı "Some Maker" (örnek) olarak bulunduğunda bir sütunda +1 biriktirmek istedim. Değilse, artış yok ancak önceki artış sonucunu göster.

Bu SQL parçası:

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

Böyle bir şey almama izin verdi:

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  

Yukarıdaki açıklama: 0 ile "bazı yapımcısı" sayısını başlatır, Bazı Maker bulunur ve +1 yaparız. Kullanıcı 1 için MakerC bulunur, bu yüzden +1 yapmayız, bunun yerine Bazı Maker'ın dikey sayısı sonraki satıra kadar 2'ye yapışır. Bölümleme Kullanıcı tarafından yapılır, bu nedenle kullanıcıyı değiştirdiğimizde birikimli sayım sıfıra döner.

İşteyim, bu cevapta herhangi bir değer istemiyorum, sadece teşekkür et ve birinin aynı durumda olması durumunda örneğimi göster. SUM ve PARTITION'ı birleştirmeye çalışıyordum, ancak "SINIRSIZ ÖNCELİK VE GÜNCEL SIRA ARASINDAKİ ROWS" sözdizimi bu görevi tamamladı.

Teşekkürler! Groaker


0

Herhangi bir türde JOIN kümülatif maaşını kullanmadan aşağıdaki sorguyu kullanarak getirin:

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

Örn: İki sütunlu bir tablonuz varsa, biri ID ve ikincisi sayıdır ve kümülatif toplamı bulmak ister.

SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.