2010-01-22 46 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 

chọn ở trên trả về cho tôi những điều sau đây.Cách nhận số tiền tích lũy

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

Làm thế nào để tôi nhận được

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

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 trong số họ khá dễ dàng. Những gì không phải là 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 đúng trong T-SQL để chạy tổng số đó là 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ừ T-SQL không tối ưu hóa cho trường hợp này. Bạn có thể nhận được O (n) bằng cách sử dụng con trỏ và/hoặc trong khi vòng lặp, nhưng sau đó bạn đang sử dụng con trỏ. (* blech! *) – RBarryYoung

Trả lời

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

Output

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

Chỉnh sửa: đây là 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ó 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ó!

+9

@Franklin Chỉ 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 của số hàng. SQL Server 2012 cho phép điều này được thực hiện hiệu quả hơn nhiều. –

+2

FWIW, tôi đã bị các khớp ngón tay của tôi đập vỡ 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, rất nhanh. Đ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 dữ liệu/nhà khoa học cần phải giải quyết vấn đề này một lần hoặc hai lần :) – BenDundee

+0

@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, ví dụ, gareths, sử dụng nó! – RedFilter

9

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. 

Returns:

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

Phiên bản mới nhất của SQL Server (2012) cho phép những điều sau đây.

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

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 

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 cho tôi.

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

+15

Đối với ngắn gọn, bạn có thể sử dụng 'ROWS UNBOUNDED PRECEDING' thay vì' ROWS GIỮA TIỀN TẠO VÀ TIỀN TỆ TIỀN '. – Dan

+0

bạn là anh hùng của tôi –

9

Cho phép đầu tiên tạo ra một bảng với 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 bảng (TỰ Gia nhập)

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 

ở đây chúng tôi đi ngay bây giờ chỉ tính tổng someValue của t2 và we`ll đượ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 và ở trên (Much better thực hiện)

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

Kết quả mong muốn

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

Drop Table CumulativeSum 

Xóa dummytable

+0

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

+0

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

+0

AFAIK bạn cần id duy nhất cho tổng tích luỹ, và bạn có thể lấy nó bằng cách sử dụ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 ) –

0

Hãy thử điều 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; 
+0

Điều này làm việc với SQL Server 2012 trở lên, 2008 đã hạn chế hỗ trợ cho các chức năng cửa sổ. –

1

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

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

Hãy thử điều 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

cumSum ........ – michaelBurns

11

Đối với SQL Server 2012 trở đi nó có thể được dễ dàng:

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

ORDER BY khoản cho SUM theo mặc định có nghĩa RANGE UNBOUNDED PRECEDING AND CURRENT ROW cho khung cửa sổ ("Ghi chú chung" tại https://msdn.microsoft.com/en-us/library/ms189461.aspx)

6

Cuối câu trả lời nhưng cho thấy một khả năng khác ...

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

trình tốt hơn so với INNER JOIN & OVER Clause khi 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 
0

Các Mà giải pháp SQL kết hợp "ROWS GIỮA ROW vô biên TRƯỚC VÀ 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 cột bất kỳ khi nào 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, tác phẩm này của 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 

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 về ở trên: Nó bắt đầu đếm "một số nhà sản xuất" với 0, Một số maker được tìm thấy và chúng tôi +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 nhưng thay vào đó số lượng theo chiều dọc của Some Maker bị mắc kẹt thành 2 cho đến hàng tiếp theo. Phân vùng là bởi Người dùng vì vậy khi chúng tôi thay đổi người dùng, số lượng tích lũy trở lại bằng không.

Tôi đang làm việc, tôi không muốn có bất kỳ bằng khen nào về câu trả lời này, chỉ cần nói lời cảm ơn và hiển thị 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à PARTITION nhưng cú pháp tuyệt vời "ROWS GIỮA TIẾP TỤC TUYỆT VỜI VÀ HIỆN TẠI" hoàn thành nhiệm vụ.

Cảm ơn! Groaker

1

Trên (Pre-SQL12) chúng ta thấy những 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 
0

Nếu không sử dụng bất kỳ loại THAM GIA lương tích lũy cho một người lấy bằng cách sử dụng truy vấn theo dõi:

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 
Các vấn đề liên quan