Cách lấy tổng cộng


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

ở trên chọn trả về cho tôi như sau.

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

Làm thế nào để tôi có được những điều sau đây:

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

5
Bắt tổng số chạy trong T-SQL không khó, có nhiều câu trả lời đúng, hầu hết đều khá dễ. Điều không dễ dàng (hoặc thậm chí có thể tại thời điểm này) là viết một truy vấn thực sự trong T-SQL để chạy các tổng số có hiệu quả. Chúng đều là O (n ^ 2), mặc dù chúng có thể dễ dàng là O (n), ngoại trừ việc T-SQL không tối ưu hóa cho trường hợp này. Bạn có thể nhận O (n) bằng cách sử dụng các con trỏ và / hoặc vòng lặp While, nhưng sau đó bạn đang sử dụng con trỏ. ( blech! )
RBarryYoung

Câu trả lời:


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

Ví dụ SQL Fiddle

Đầu ra

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

Chỉnh sửa: đây là một giải pháp tổng quát sẽ hoạt động trên hầu hết các nền tảng db. Khi có một giải pháp tốt hơn có sẵn cho nền tảng cụ thể của bạn (ví dụ: gareth's), hãy sử dụng nó!


12
@Franklin Chỉ có chi phí hiệu quả cho các bảng nhỏ. Chi phí tăng tỷ lệ thuận với bình phương số lượng hàng. SQL Server 2012 cho phép điều này được thực hiện hiệu quả hơn nhiều.
Martin Smith

3
FWIW, tôi đã bị gãy ngón tay khi làm điều này bởi một DBA. Tôi nghĩ lý do là nó thực sự đắt tiền, thực sự nhanh chóng. Điều đó đang được nói, đây là một câu hỏi phỏng vấn tuyệt vời, vì hầu hết các nhà phân tích / nhà khoa học dữ liệu nên đã phải giải quyết vấn đề này một hoặc hai lần :)
BenDundee

@BenDundee Đồng ý - Tôi có xu hướng cung cấp các giải pháp SQL tổng quát sẽ hoạt động trên hầu hết các nền tảng db. Như mọi khi, khi có một cách tiếp cận tốt hơn có sẵn, ví dụ, gareths, hãy sử dụng nó!
RedFilter

198

Phiên bản mới nhất của SQL Server (2012) cho phép như sau.

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

hoặc là

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

Điều này thậm chí còn nhanh hơn. Phiên bản phân vùng hoàn thành trong 34 giây trên 5 triệu hàng đối với tôi.

Cảm ơn Peso, người đã nhận xét về chủ đề Nhóm SQL được đề cập trong một câu trả lời khác.


22
Để cho ngắn gọn, bạn có thể sử dụng ROWS UNBOUNDED PRECEDINGthay vì ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
Dan

1
Lưu ý: Nếu cột bạn muốn tính tổng cộng thì chính nó đã là tổng hoặc đếm, bạn có thể gói toàn bộ nội dung dưới dạng truy vấn bên trong hoặc bạn thực sự có thể làm SUM(COUNT(*)) OVER (ORDER BY RowId ROWS UNBOUNDED PRECEDING) AS CumulativeSum. Nó không rõ ràng ngay lập tức với tôi nếu nó hoạt động nhưng nó đã làm :-)
Simon_Weaver

Có sẵn trong PostgreSQL kể từ 8.4: postgresql.org/docs/8.4/sql-select.html
ADJenks


13

Một phiên bản CTE, chỉ để cho vui:

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

Trả về:

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

12

Trước tiên hãy tạo một bảng có dữ liệu giả ->

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 

Ở đây tôi đang tham gia cùng một bảng (Tham gia TỰ TIN)

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

KẾT QUẢ :

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

Bây giờ chúng ta đi chỉ cần tổng hợp Somevalue của t2 và chúng ta sẽ nhận được 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

CHO SQL SERVER 2012 trở lên (Thực hiện tốt hơn nhiều)

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

Kết quả như ý

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

Drop Table CumulativeSum

Xóa dummytable


vui lòng chỉnh sửa câu trả lời của bạn và định dạng mã để có thể đọc được
kleopatra

Điều gì xảy ra nếu giá trị mi "ID" được lặp lại? (chúng không phải là khóa chính trong bảng của tôi) Tôi không thể điều chỉnh truy vấn này cho trường hợp đó?
pablete

AFAIK bạn cần id duy nhất cho tổng số tích lũy và bạn có thể lấy nó bằng row_number. kiểm tra xem mã bên dưới:; với NewTBLWITHUNiqueID như (chọn row_number () trên (thứ tự theo id, somevalue) UniqueID, * Từ CUMULATIVESUMwithoutPK)
Neeraj Prasad Sharma

Cảm ơn @NeerajPrasadSharma, tôi thực sự đã sử dụng rank()và một mệnh lệnh khác theo mệnh đề để giải quyết nó.
pablete

5

Trả lời muộn nhưng cho thấy một khả năng nữa ...

Tạo Sum tích lũy có thể được tối ưu hóa hơn với CROSS APPLY logic.

Hoạt động tốt hơn so với INNER JOIN& OVER Clausekhi phân tích kế hoạch truy vấn thực tế ...

/* 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
Tôi không bị thuyết phục. "Chi phí truy vấn liên quan đến lô" là một điều vô nghĩa để so sánh hiệu suất của các truy vấn. Chi phí truy vấn là ước tính được sử dụng bởi trình lập kế hoạch truy vấn để nhanh chóng cân nhắc các kế hoạch khác nhau và chọn ít tốn kém nhất, nhưng các chi phí đó là để so sánh các kế hoạch cho cùng một truy vấn và hoàn toàn không liên quan hoặc có thể so sánh giữa các truy vấn . Bộ dữ liệu mẫu này cũng quá nhỏ để thấy bất kỳ sự khác biệt đáng kể nào giữa ba phương pháp. Hãy thử lại với hàng 1m, xem xét các kế hoạch thực hiện thực tế, thử set io statistics onvà so sánh cpu và thời gian thực tế.
Davos

4

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


Đó là một cách rất thông minh để đạt được kết quả và bạn có thể thêm nhiều điều kiện vào tổng.
RaRdEvA

@RaRdEvA Mặc dù vậy, nó không tuyệt vời cho hiệu năng, nó chạy trên correlated subquerymỗi hàng của tập kết quả, quét càng nhiều hàng hơn khi nó đi. Nó không giữ tổng số chạy và quét dữ liệu một lần như các chức năng của cửa sổ có thể.
Davos

1
@Davos bạn đúng, nếu bạn sử dụng nó sẽ bị chậm hơn 100.000 hồ sơ.
RaRdEvA


1

Khi bảng được tạo -

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

Ở trên (Pre-SQL12) chúng ta thấy các ví dụ như thế này: -

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

Hiệu quả hơn...

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

1

Bạn có thể sử dụng truy vấn đơn giản này để tính toán lũy tiến:

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

0

Thử cái này

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;

Điều này hoạt động với SQL Server 2012 trở lên, 2008 đã hỗ trợ hạn chế cho các chức năng của cửa sổ.
Peter Smit

0

Thử cái này:

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

Giải pháp SQL kết hợp "ROWS GIỮA CHÍNH XÁC CHÍNH XÁC VÀ ROW HIỆN TẠI" và "SUM" đã làm chính xác những gì tôi muốn đạt được. Cảm ơn bạn rất nhiều!

Nếu nó có thể giúp bất cứ ai, đây là trường hợp của tôi. Tôi muốn tích lũy +1 trong một cột bất cứ khi nào một nhà sản xuất được tìm thấy là "Một số nhà sản xuất" (ví dụ). Nếu không, không tăng nhưng hiển thị kết quả tăng trước đó.

Vì vậy, đoạn SQL này:

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

Cho phép tôi có được một cái gì đó như thế này:

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  

Giải thích ở trên: Nó bắt đầu đếm "một số nhà sản xuất" với 0, Một số Nhà sản xuất được tìm thấy và chúng tôi thực hiện +1. Đối với Người dùng 1, MakerC được tìm thấy vì vậy chúng tôi không thực hiện +1 mà thay vào đó, số lượng dọc của Một số Trình tạo bị kẹt ở 2 cho đến hàng tiếp theo. Phân vùng là bởi Người dùng nên khi chúng tôi thay đổi người dùng, số tích lũy sẽ trở về không.

Tôi đang ở nơi làm việc, tôi không muốn có bất kỳ công trạng nào cho câu trả lời này, chỉ cần nói cảm ơn và đưa ra ví dụ của tôi trong trường hợp ai đó ở trong tình huống tương tự. Tôi đã cố gắng kết hợp SUM và PHẦN THAM GIA nhưng cú pháp tuyệt vời "ROWS GIỮA CHÍNH XÁC CHÍNH XÁC VÀ ROWENT HIỆN TẠI" đã hoàn thành nhiệm vụ.

Cảm ơn! Thợ làm bánh


0

Không sử dụng bất kỳ loại lương tích lũy THAM GIA nào cho một người tìm nạp bằng cách sử dụng truy vấn theo sau:

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

Ví dụ: NẾU bạn có một bảng có hai cột, một cột là ID và thứ hai là số và muốn tìm ra tổng tích lũy.

SELECT ID,Number,SUM(Number)OVER(ORDER BY ID) FROM T
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.