누적 합계를 얻는 방법


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

위의 선택은 다음을 반환합니다.

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로 실제 쿼리를 작성하는 것입니다. T-SQL이이 경우에 최적화되지 않는다는 점을 제외하고는 모두 O (n ^ 2) 일 수 있지만 모두 O (n ^ 2)입니다. 커서 및 / 또는 While 루프를 사용하여 O (n)을 얻을 수 있지만 커서를 사용하고 있습니다. (
blech

답변:


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 피들 예제

산출

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

편집 : 이것은 대부분의 DB 플랫폼에서 작동하는 일반화 된 솔루션입니다. 특정 플랫폼 (예 : gareth)에 적합한 솔루션이 더 있으면 사용하십시오!


12
@Franklin 작은 테이블에 대해서만 비용 효율적입니다. 비용은 행 수의 제곱에 비례하여 증가합니다. SQL Server 2012에서는이 작업을 훨씬 더 효율적으로 수행 할 수 있습니다.
Martin Smith

3
FWIW, 나는 DBA 가이 작업을 수행 할 때 너클을 쳤다. 그 이유는 정말 비싸고 빠르기 때문이라고 생각합니다. 대부분의 데이터 분석가 / 과학자
들이이

@ BenDundee Agreed-대부분의 DB 플랫폼에서 작동하는 일반화 된 SQL 솔루션을 제공하는 경향이 있습니다. 언제나처럼 가레스와 같은 더 나은 접근 방법이있을 때 사용하십시오!
RedFilter

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

이것은 더 빠릅니다. 분할 된 버전은 5 백만 행이 넘는 34 초 만에 완료됩니다.

다른 답변에서 언급 된 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. 그것이 효과가 있다면 그것은 나에게 분명하지 않았지만 :-)
Simon_Weaver 5

8.4 이후 PostgreSQL에서 사용 가능 : 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

FOR 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

mi "ID"값이 반복되면 어떻게됩니까? (내 테이블에 기본 키가 분명하지 않습니다.)이 쿼리를 해당 경우에 적용 할 수 없었습니까?
pablete

AFAIK 누적 sum에 고유 ID가 필요하며 row_number를 사용하여 얻을 수 있습니다. 아래의 코드를 확인하십시오.; NewTBLWITHUNiqueID를 (row_number () over (order by id, somevalue) UniqueID 선택, * CUMULATIVESUMwithoutPK에서 선택)
Neeraj Prasad Sharma

감사합니다 @NeerajPrasadSharma, 나는 실제로 rank()그것을 해결하기 위해 또 다른 order by 조항을 사용했습니다.
pablete

5

답변이 늦었지만 한 가지 가능성이 더 있습니다 ...

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
설득되지 않았습니다. "일괄 처리에 대한 쿼리 비용"은 쿼리 성능을 비교하는 데 의미가 없습니다. 쿼리 비용은 쿼리 계획자가 다른 계획을 신속하게 계량하고 가장 비용이 적게 드는 것을 선택하는 데 사용되는 추정치이지만, 같은 쿼리에 대한 계획을 비교하는 데 드는 비용이며 쿼리 간에는 관련이 없거나 비교할 없습니다. 이 샘플 데이터 세트도 너무 작아서 세 가지 방법 사이에 중요한 차이가 없습니다. 1m 행으로 다시 시도하고 실제 실행 계획을보고 시도 set io statistics on하고 CPU와 실제 시간을 비교하십시오.
Davos

4

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


결과를 얻는 매우 현명한 방법이며 합계에 여러 조건을 추가 할 수 있습니다.
RaRdEvA

@RaRdEvA 성능에 좋지는 않지만 correlated subquery결과 집합의 모든 단일 행에 대해 실행 하여 점점 더 많은 행을 스캔합니다. 그것은 총계를 유지하지 않고 윈도우 기능처럼 데이터를 한 번 스캔합니다.
Davos

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

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 솔루션은 "바인딩되지 않은 선행 및 현재 행 사이의 행"과 "SUM"이 내가 원하는 것을 정확히 수행했습니다. 정말 고맙습니다!

그것이 누군가를 도울 수 있다면, 여기 내 사건이 있습니다. 메이커가 "일부 메이커"(예)로 표시 될 때마다 열에서 +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에 고정됩니다. 파티셔닝은 사용자 별이므로 사용자를 변경하면 누적 횟수가 0으로 돌아갑니다.

나는 일하고 있습니다.이 답변에 대한 공로를 원하지 않습니다. 누군가가 같은 상황에 처한 경우에 대비하여 감사하고 내 예를 보여주십시오. 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

예 : 열이 두 개인 테이블이있는 경우 하나는 ID이고 두 번째는 숫자이며 누적 합계를 찾으려고합니다.

SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.