Як отримати сукупну суму


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 для ефективного запуску підсумків. Всі вони є O (n ^ 2), хоча вони легко можуть бути O (n), за винятком того, що T-SQL не оптимізує для цього випадку. Ви можете отримати O (n) за допомогою курсорів та / або циклів Хоча, але тоді ви використовуєте Курсори. ( 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 Економічно лише для невеликих столиків. Вартість зростає пропорційно квадрату кількості рядів. SQL Server 2012 дозволяє зробити це набагато ефективніше.
Мартін Сміт

3
FWIW, у мене були зіпсовані суглоби, коли це робив DBA. Я думаю, що причина - це стає дуже дорого, дуже швидко. Однак, це велике запитання про інтерв'ю, оскільки більшість аналітиків даних / вчених повинні були вирішити цю проблему один-два рази :)
BenDundee,

@BenDundee Погодився - я прагну надавати узагальнені рішення SQL, які працюватимуть на більшості платформ db. Як завжди, коли є кращий підхід, наприклад, гарети, використовуйте його!
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

Це ще швидше. Версія з розділеною версією завершує за 34 секунди понад 5 мільйонів рядків для мене.

Дякую Песо, який прокоментував нитку SQL Team, про яку йдеться в іншій відповіді.


22
Для стислості ви можете використовувати ROWS UNBOUNDED PRECEDINGзамість цього ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
День

1
Примітка. Якщо стовпець, який ви хочете підсумовувати підсумком, вже є сумою чи рахунком, ви можете або обернути всю справу як внутрішній запит, або ви можете насправді зробити SUM(COUNT(*)) OVER (ORDER BY RowId ROWS UNBOUNDED PRECEDING) AS CumulativeSum. Мені не відразу було очевидно, чи буде це працювати, але це так :-)
Simon_Weaver

Доступно у 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 і отримаємо анс

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" повторюються? (вони явно не є первинним ключем у моїй таблиці). Я не зміг адаптувати цей запит до цього випадку?
pablete

AFAIK вам потрібен унікальний ідентифікатор для накопичувальної суми, і ви можете отримати його, використовуючи row_number. перевірити , що код нижче :; з NewTBLWITHUNiqueID як (виберіть row_number () над (замовлення за ідентифікатором, SomeValue) UniqueID, * Від CUMULATIVESUMwithoutPK)
Neeraj Прасад Шарма

Дякую @NeerajPrasadSharma, я фактично використовував rank()і інше замовлення за допомогою пункту, щоб вирішити його.
палітра

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
Мене не переконують. "Вартість запиту щодо партії" - це безглузда річ порівняння ефективності запитів. Витрати на запити - це оцінки, що використовуються планувальником запитів для швидкого зважування різних планів та вибору найменш затратних, але ці витрати призначені для порівняння планів одного і того ж запиту і взагалі не є релевантними або порівнянними між запитами . Цей вибірковий набір даних також занадто крихітний, щоб побачити будь-яку суттєву різницю між трьома методами. Спробуйте ще раз з 1м рядків, подивіться на фактичні плани виконання, спробуйте його set io statistics onта порівняйте процесор та фактичний час.
Давос,

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

Вище (Pre-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 має обмежену підтримку віконних функцій.
Пітер Сміт

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, яке поєднує в собі "ПРАВИЛЬНІ МЕЖДУ НЕ ОБ'ЄДНАНИМ ПЕРЕДБАЧЕННЯМ І ТОЧНИМ РЯДОМ" та "СУМ" зробили саме те, чого я хотів досягти. Дуже дякую!

Якщо це може допомогти комусь, ось моя справа. Я хотів зібрати +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, але замість цього вертикальний підрахунок Some Maker дотримується 2 до наступного рядка. Розмежування здійснюється користувачем, тому коли ми змінюємо користувача, сукупний підрахунок повертається до нуля.

Я на роботі, я не хочу ніякої заслуги щодо цієї відповіді, просто скажу дякую та покажу свій приклад, якщо хтось опинився в тій же ситуації. Я намагався поєднати СУМ і ЧАСТИНЮ, але дивовижний синтаксис "РАДИ МЕЖУ НЕ ОБ'ЄДНАНОГО ПРОГРАММУВАННЯ ТА ТОЧНОГО РЯДУ" виконав завдання.

Дякую! Грукер


0

Без використання будь-якого типу ПРИЄДНАЙТЕ накопичувальну зарплату для особи, яка отримує, використовуючи наступний запит:

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

Для Ex: Якщо у вас є таблиця з двома стовпцями, один - ID, а другий - число і хоче дізнатися сукупну суму.

SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.