累計を取得する方法


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

上記のselectは私に次を返します。

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

次の方法で入手できます。

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

5
T-SQLで合計を取得することは難しくありません。多くの正解があり、そのほとんどは非常に簡単です。簡単ではない(または現時点では不可能)のは、T-SQLで真のクエリを記述して、効率的な合計を実行することです。それらはすべてO(n ^ 2)ですが、T-SQLがこの場合に最適化されないことを除いて、簡単にO(n)になる可能性があります。CursorsやWhileループを使用してO(n)を取得できますが、その後Cursorsを使用しています。(blech!
RBarryYoung

回答:


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の例

出力

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

編集:これは、ほとんどのdbプラットフォームで機能する一般化されたソリューションです。特定のプラットフォーム(例:gareth's)で利用できるより良いソリューションがある場合は、それを使用してください!


12
@Franklin小さなテーブルに対してのみ費用対効果が高い。コストは行数の2乗に比例して増加します。SQL Server 2012では、これをより効率的に行うことができます。
マーティン・スミス

3
FWIW、私はこれをDBAが行うときにナックルをぶつけました。その理由は、それが非常に高価で、非常に速くなるからだと思います。そうは言っても、ほとんどのデータアナリスト/科学者はこの問題を1回または2回解決する必要があったので、これは素晴らしいインタビューの質問です:)
BenDundee

@BenDundee同意-ほとんどのdbプラットフォームで機能する汎用SQLソリューションを提供する傾向があります。いつものように、ガレスなど、より良いアプローチが利用できる場合は、それを使用してください!
RedFilter 2015

199

SQL Serverの最新バージョン(2012)では、次のことが許可されています。

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

または

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

これはさらに高速です。パーティションバージョンは34秒間で500万行以上で完了します。

別の回答で言及されているSQLチームスレッドについてコメントしてくれたPesoに感謝します。


22
簡潔にするためROWS UNBOUNDED PRECEDINGに、の代わりにを使用できますROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
Dan

1
注:累計する列自体が既に合計またはカウントである場合は、全体を内部クエリとしてラップするか、実際に行うことができますSUM(COUNT(*)) OVER (ORDER BY RowId ROWS UNBOUNDED PRECEDING) AS CumulativeSum。うまくいくかどうかはすぐにはわかりませんでしたが、うまく
いきまし

PostgreSQL 8.4以降で利用可能:postgresql.org/docs/8.4/sql-select.html
ADJenks


13

楽しみのためのCTEバージョン:

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

戻り値:

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

13

最初にダミーデータを含むテーブルを作成しましょう->

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 

ここで同じテーブルに参加しています(SELF Joining)

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

結果:

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

ここでは、t2のSomevalueを合計し、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

SQL SERVER 2012以降(パフォーマンスが大幅に向上)

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

望ましい結果

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

Drop Table CumulativeSum

ダミーテーブルをクリアする


解答を編集し、コードを読みやすい形式にしてください
kleopatra

「ID」の値が繰り返される場合はどうなりますか?(テーブルの主キーではありません)このクエリをその場合に適応させることができませんか?
2014年

私の知る限り、累積合計に一意のIDが必要です。row_numberを使用して取得できます。以下のコードを確認してください:; with NewTBLWITHUNiqueID as(select row_number()over(order by id、somevalue)UniqueID、* From CUMULATIVESUMwithoutPK)
Neeraj Prasad Sharma

@NeerajPrasadSharmaに感謝します、私は実際にrank()それを解決するために節と別のorder by句を使用しました。
2014年

5

遅い答えですが、もう1つの可能性を示しています...

累積合計の生成は、 CROSS APPLYロジックを。

INNER JOIN&よりもうまく機能OVER Clauseする実際のクエリプランを分析すると...

/* 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
私は説得されません。「バッチに対するクエリコスト」は、クエリのパフォーマンスを比較する意味がありません。クエリコストは、さまざまなプランをすばやく比較して最もコストの低いものを選択するためにクエリプランナーが使用する推定値ですが、これらのコストは同じクエリのプランを比較するためのものであり、クエリ間で関連性や比較性はなく、まったく関係ありません。このサンプルデータセットも小さすぎて、3つの方法の間に有意差は見られません。1m行でもう一度試して、実際の実行計画を見てset io statistics on、CPUと実際の時間を比較してみてください。
ダボス

4

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


これは結果を得る非常にスマートな方法であり、合計に複数の条件を追加できます。
RaRdEvA

@RaRdEvAただし、パフォーマンスに優れているわけではありません。correlated subquery結果セットのすべての行に対してそれを実行し、行をスキャンしていきます。それは、現在までの合計を保持せず、ウィンドウ関数のように一度データをスキャンします。
ダボス、

1
@Davos正解です。使用すると、100,000レコードを超える非常に遅くなります。
RaRdEvA


2

この単純なクエリをプログレッシブ計算に使用できます。

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

1

テーブルが作成されたら-

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

上記(SQL12以前)には、次のような例があります。

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

もっと効率的...

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

これを試して

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;

これはSQL Server 2012以降で動作します。2008では、ウィンドウ関数のサポートが制限されています。
Peter Smit 2016年

0

これを試して:

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

SQLソリューションは、「無制限の先行と現在の行の間にある行」と「合計」を組み合わせたもので、まさに私が達成したかったものです。どうもありがとうございます!

それが誰かを助けることができるなら、ここに私のケースがありました。メーカーが「Some Maker」(例)として見つかったときはいつでも、列に+1を累積したいと思っていました。そうでない場合、増分はありませんが、前の増分結果が表示されます。

したがって、この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

私はこのようなものを得ることができました:

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  

上記の説明:「一部のメーカー」のカウントを0から開始します。一部のメーカーが見つかり、+ 1します。ユーザー1の場合、MakerCが見つかったため、+ 1は行いませんが、代わりに、次の行まで、一部のメーカーの垂直カウントが2に固定されています。パーティション化はユーザーごとなので、ユーザーを変更すると、累積カウントはゼロに戻ります。

私は仕事中です。この回答にメリットはありません。感謝の意を表し、誰かが同じ状況にある場合の例を示します。私はSUMとPARTITIONを組み合わせようとしましたが、驚くべき構文「アンバウンドされた先行と現在の行の間の行」がタスクを完了しました。

ありがとう!グローカー


0

次のクエリを使用して、個人フェッチにどのタイプのJOIN累積給与も使用しない場合:

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

例:2つの列を持つテーブルがある場合、1つはIDで、2つ目は数値であり、累積合計を求めます。

SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.