如何获得累计金额


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

上面的选择给我以下内容。

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),尽管它们很容易是O(n),但T-SQL并未针对这种情况进行优化。您可以使用游标和/或While循环获得O(n),但随后您使用的是游标。(blech!
RBarryYoung 2010年

Answers:


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

输出量

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

编辑:这是一个通用的解决方案,将可在大多数数据库平台上使用。如果有针对您的特定平台(例如gareth的)的更好解决方案,请使用它!


12
@富兰克林只有小桌子才有成本效益。成本与行数的平方成正比。SQL Server 2012允许更高效地完成此操作。
马丁·史密斯

3
FWIW,DBA执行此操作时,我的指关节被打了一下。我认为原因是它变得非常昂贵,非常快。话虽这么说,这是一个很好的面试问题,因为大多数数据分析员/科学家都必须解决一次或两次此问题:)
BenDundee 2015年

@BenDundee同意-我倾向于提供适用于大多数数据库平台的通用SQL解决方案。与往常一样,当有更好的方法(例如gareths)时,请使用它!
RedFilter

198

最新版本的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万行。

感谢Peso,他评论了另一个答案中提到的SQL Team线程。


22
为简便起见,您可以使用ROWS UNBOUNDED PRECEDING代替ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
2014年

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

12

首先创建一个包含伪数据的表->

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加入)

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 2012年

如果mi“ ID”值重复,该怎么办?(它们显然不是我的表中的主键)我无法使该查询适应这种情况吗?
pablete

AFAIK您需要唯一的ID来累积总和,您可以使用row_number获得它。检查以下代码:; NewTBLWITHUNiqueID为(选择row_number()over(按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和实际时间。
达沃斯

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


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开始“ some maker”的计数,找到Some Maker,我们做+1。对于用户1,找到了MakerC,因此我们不执行+1,而是将Some Maker的垂直计数固定为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

例如:如果您有一个包含两列的表,其中一列是ID,第二列是数字,并想找出累积和。

SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.