2010-01-22 41 views
120
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 

powyższa opcja zwraca mi następującą.Jak uzyskać skumulowaną sumę

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

Jak uzyskać następujące

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

Uzyskiwanie sum ogólnych w T-SQL nie jest trudne, istnieje wiele poprawnych odpowiedzi, większość z nich jest całkiem łatwa. Co nie jest łatwe (a nawet możliwe w tym momencie) jest napisanie prawdziwego zapytania w T-SQL, aby wydajne sumy uruchomione. Wszystkie są O (n^2), choć mogą być łatwo O (n), z tym wyjątkiem, że T-SQL nie optymalizuje dla tego przypadku. Możesz uzyskać O (n) za pomocą kursorów i/lub pętli While, ale wtedy używasz kursorów. (* blech! *) – RBarryYoung

Odpowiedz

162
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 example

Wyjście

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

Edycja: jest to uogólnione rozwiązanie, które będzie działać na większości platform db. Gdy dostępne jest lepsze rozwiązanie dla konkretnej platformy (np. Garetha), użyj go!

+9

@Franklin Ekonomiczne tylko dla małych stołów. Koszt rośnie proporcjonalnie do kwadratu liczby rzędów. SQL Server 2012 pozwala na to znacznie wydajniej. –

+2

FWIW, Miałem już kłyknięcia, gdy robił to DBA. Myślę, że powodem jest to, że robi się naprawdę drogi, naprawdę szybki. W związku z tym jest to świetne pytanie wywiadowe, ponieważ większość analityków/naukowców danych musiała raz lub dwa razy rozwiązać ten problem :) – BenDundee

+0

@BenDundee Zgoda - Mam tendencję do dostarczania ogólnych rozwiązań SQL, które będą działać na większości platform db. Jak zawsze, gdy dostępne jest lepsze podejście, np. Gareths, użyj go! – RedFilter

9

Wersja CTE, tylko dla zabawy:

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

Powroty:

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

Najnowsza wersja SQL Server (2012) pozwala na następujące.

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

lub

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 

to jeszcze szybciej. Partycjonowana wersja kończy się w 34 sekund ponad 5 milionów wierszy dla mnie.

Podziękowania dla Peso, który skomentował wątek zespołu SQL, o którym mowa w innej odpowiedzi.

+15

Dla zwięzłości możesz użyć 'ROWS UNBOUNDED PRECEDING' zamiast' ROWS MIĘDZY UNBOUNDED PRECEDING I BIEŻĄCE RZĘDZIE'. – Dan

+0

jesteś moim bohaterem –

9

Spójrzmy najpierw utworzyć tabelę z manekina danych ->

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 

jestem tutaj łączącą samej tabeli (self Łączenie)

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

WYNIK:

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 

jedziemy teraz po prostu zsumować SomeValue T2 i `ll dostać 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 

dla SQL Server 2012 i nowszych (znacznie lepiej wykonywać)

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

Pożądany wynik

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

Drop Table CumulativeSum 

Wyczyść dummytable

+0

edytuj odpowiedź i sformatuj kod tak, aby był czytelny – kleopatra

+0

Co jeśli wartości "ID" zostaną powtórzone? (są one obvoiusly nie kluczem podstawowym w mojej tabeli) Nie byłem w stanie dostosować tego zapytania do tego przypadku? – pablete

+0

AFAIK potrzebujesz unikalnego identyfikatora do skumulowanej sumy, a możesz go uzyskać za pomocą row_number. sprawdzić, czy kod poniżej: ; z NewTBLWITHUNiqueID jak ( wybierz ROW_NUMBER() nad (zamówienia przez id, someValue) UniqueID, * Od CUMULATIVESUMwithoutPK ) –

0

Spróbuj

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; 
+0

Działa to z SQL Server 2012 i nowszych, 2008 ma ograniczone wsparcie dla funkcji okna. –

1

Po utworzeniu tabeli -

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 
2

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

0

Spróbuj tego:

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

cumSum ........ – michaelBurns

11

Dla SQL Server 2012 roku może to być proste:

SELECT id, SomeNumt, sum(SomeNumt) OVER (ORDER BY id) as CumSrome FROM @t 

ponieważ ORDER BY klauzula dla SUM domyślnie oznacza RANGE UNBOUNDED PRECEDING AND CURRENT ROW do ramy okiennej ("Uwagi ogólne" na https://msdn.microsoft.com/en-us/library/ms189461.aspx)

6

Późne odpowiedź, ale ukazujący jeden więcej możliwości ...

Generowanie łącznej sumy można zoptymalizować za pomocą logiki CROSS APPLY.

działa lepiej niż INNER JOIN & OVER Clause gdy analizowanym rzeczywistego planu kwerend ...

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

wich rozwiązanie SQL łączy „Wierszy MIĘDZY nieograniczona poprzedzające i bieżący wiersz” i „Suma” zrobił dokładnie to, co chciałem osiągnąć. Dziękuję bardzo!

Jeśli to może pomóc każdemu, oto moja sprawa. Chciałem kumulować +1 w kolumnie, gdy twórca zostanie znaleziony jako "Some Maker" (przykład). Jeśli nie, brak przyrostu, ale wynik poprzedniej inkrementacji.

Więc ten kawałek 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 

dozwolone żebym coś takiego:

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 

Wyjaśnienie powyżej: Zaczyna się liczyć z "pewnym maker" 0, Some Maker zostanie znaleziony, a my wykonamy +1. W przypadku użytkownika 1 znajduje się MakerC, więc nie robimy +1, ale zamiast tego liczba pionków niektórych twórców wynosi 2 do następnego rzędu. Partycjonowanie jest według użytkownika, więc po zmianie użytkownika łączna liczba wraca do zera.

Jestem w pracy, nie chcę żadnych zasług na tę odpowiedź, po prostu powiedz dziękuję i pokaż mój przykład na wypadek, gdyby ktoś znalazł się w takiej samej sytuacji. Próbowałem połączyć SUMA i PARTYCJĘ, ale niesamowita składnia "ROWS MIĘDZY UNBOUNDED PRECEDING AND CURRENT ROW" zakończyła zadanie.

Dzięki! Groaker

1

powyżej (Pre-SQL12) widzimy przykłady tak: -

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

Bardziej efektywne ...

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

bez użycia jakiegokolwiek rodzaju DOŁĄCZ skumulowanego wynagrodzenia dla osoby pobrania za pomocą następującej kwerendy:

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 
Powiązane problemy