Tính Tổng tích lũy trong PostgreSQL


85

Tôi muốn tìm số lượng trường tích lũy hoặc đang chạy và chèn nó từ giai đoạn vào bảng. Cấu trúc dàn dựng của tôi giống như sau:

ea_month    id       amount    ea_year    circle_id
April       92570    1000      2014        1
April       92571    3000      2014        2
April       92572    2000      2014        3
March       92573    3000      2014        1
March       92574    2500      2014        2
March       92575    3750      2014        3
February    92576    2000      2014        1
February    92577    2500      2014        2
February    92578    1450      2014        3          

Tôi muốn bảng mục tiêu của mình trông giống như sau:

ea_month    id       amount    ea_year    circle_id    cum_amt
February    92576    1000      2014        1           1000 
March       92573    3000      2014        1           4000
April       92570    2000      2014        1           6000
February    92577    3000      2014        2           3000
March       92574    2500      2014        2           5500
April       92571    3750      2014        2           9250
February    92578    2000      2014        3           2000
March       92575    2500      2014        3           4500
April       92572    1450      2014        3           5950

Tôi thực sự rất bối rối không biết làm thế nào để đạt được kết quả này. Tôi muốn đạt được kết quả này bằng cách sử dụng PostgreSQL.

Bất cứ ai có thể gợi ý làm thế nào để đạt được tập hợp kết quả này?


1
Làm cách nào để bạn có được cum_amount 1000 trong bảng mục tiêu của mình? Đối với circle_id, số tiền có vẻ là 2000.

Câu trả lời:


130

Về cơ bản, bạn cần một chức năng cửa sổ . Đó là một tính năng tiêu chuẩn ngày nay. Ngoài các chức năng cửa sổ chính hãng, bạn có thể sử dụng bất kỳ chức năng tổng hợp nào làm chức năng cửa sổ trong Postgres bằng cách thêm một OVERđiều khoản.

Khó khăn đặc biệt ở đây là lấy phân vùng và sắp xếp thứ tự đúng:

SELECT ea_month, id, amount, ea_year, circle_id
     , sum(amount) OVER (PARTITION BY circle_id
                         ORDER BY ea_year, ea_month) AS cum_amt
FROM   tbl
ORDER  BY circle_id, month;

không GROUP BY .

Tổng cho mỗi hàng được tính từ hàng đầu tiên trong phân vùng đến hàng hiện tại - hoặc trích dẫn hướng dẫn cho chính xác:

Tùy chọn khung mặc định là RANGE UNBOUNDED PRECEDING, tương tự như RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Với ORDER BY, điều này đặt khung là tất cả các hàng từ phân vùng bắt đầu đến ORDER BYđồng đẳng cuối cùng của hàng hiện tại .

... là số tiền tích lũy hoặc đang chạy mà bạn đang theo đuổi. Tôi nhấn mạnh đậm.

Các hàng có cùng (circle_id, ea_year, ea_month)"ngang hàng" trong truy vấn này. Tất cả chúng hiển thị cùng một tổng đang chạy với tất cả các đồng nghiệp được thêm vào tổng. Nhưng tôi giả sử bảng của bạn đang UNIQUEbật (circle_id, ea_year, ea_month), khi đó thứ tự sắp xếp là xác định và không có hàng nào có giá trị ngang hàng.

Bây giờ, ORDER BY ... ea_month sẽ không hoạt động với các chuỗi cho tên tháng . Postgres sẽ sắp xếp theo thứ tự bảng chữ cái theo cài đặt ngôn ngữ.

Nếu bạn có các dategiá trị thực được lưu trữ trong bảng của mình, bạn có thể sắp xếp đúng cách. Nếu không, tôi đề nghị để thay thế ea_yearea_monthvới một cột duy nhất moncủa loại hình datetrong bảng.

  • Biến đổi những gì bạn có với to_date():

      to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • Để hiển thị, bạn có thể lấy các chuỗi gốc bằng to_char():

      to_char(mon, 'Month') AS ea_month
      to_char(mon, 'YYYY') AS ea_year
    

Trong khi bị mắc kẹt với thiết kế không may, điều này sẽ hoạt động:

SELECT ea_month, id, amount, ea_year, circle_id
     , sum(amount) OVER (PARTITION BY circle_id ORDER BY mon) AS cum_amt
FROM   (SELECT *, to_date(ea_year || ea_month, 'YYYYMonth') AS mon FROM tbl)
ORDER  BY circle_id, mon;

Cảm ơn vì giải pháp .. Bạn có thể giúp tôi một điều nữa. Tôi muốn thực hiện điều tương tự bằng cách sử dụng con trỏ với logic là mỗi vòng tròn sẽ chỉ có một bản ghi cho một tháng trong năm. Và chức năng được cho là chạy mỗi tháng một lần. Làm thế nào tôi có thể đạt được điều này?
Yousuf Sultan

4
@YousufSultan: Hầu hết thời gian có một giải pháp tốt hơn là con trỏ. Đó chắc chắn là công cụ cho một câu hỏi mới. Hãy bắt đầu một câu hỏi mới.
Erwin Brandstetter

Tôi thấy câu trả lời này không đầy đủ mà không có ít nhất một lưu ý rằng có "đóng khung" đang diễn ra ở đây mà mặc định range unbounded precedinglà, giống như range between unbounded preceding and current row. Đây là lý do tại sao sum()khi được sử dụng như một hàm cửa sổ tạo ra tổng số đang chạy - trong khi các hàm cửa sổ khác không có khung mặc định này.
Colin 't Hart

1
@ Colin'tHart: Tôi đã thêm một số chi tiết ở trên để làm rõ.
Erwin Brandstetter

Dưới đây là một liên kết đến một câu hỏi tương tự với một truy vấn đơn giản (các PARTITIONkhông phải luôn luôn cần thiết để tạo ra một tổng chạy): stackoverflow.com/a/5700744/175830
Jason Axelson
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.