การคำนวณผลรวมสะสมใน PostgreSQL


87

ฉันต้องการค้นหาจำนวนฟิลด์สะสมหรือกำลังทำงานและแทรกจากการจัดเตรียมเป็นตาราง โครงสร้างการแสดงละครของฉันเป็นดังนี้:

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          

ฉันต้องการให้ตารางเป้าหมายของฉันมีลักษณะดังนี้:

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

ฉันสับสนมากกับวิธีการบรรลุผลนี้ ฉันต้องการบรรลุผลลัพธ์นี้โดยใช้ PostgreSQL

ใครช่วยแนะนำวิธีการบรรลุผลชุดนี้ได้บ้าง


1
คุณจะได้รับ cum_amount ของ 1,000 ในตารางเป้าหมายของคุณได้อย่างไร? สำหรับ circle_id จำนวนเงินน่าจะเป็น 2000

คำตอบ:


132

โดยทั่วไปคุณต้องมีฟังก์ชั่นหน้าต่าง นั่นเป็นคุณสมบัติมาตรฐานในปัจจุบัน นอกเหนือจากฟังก์ชันหน้าต่างแท้แล้วคุณสามารถใช้ฟังก์ชันการรวมใด ๆเป็นฟังก์ชันหน้าต่างใน Postgres ได้โดยการต่อท้ายOVERอนุประโยค

ความยากพิเศษที่นี่คือการรับพาร์ติชันและเรียงลำดับให้ถูกต้อง:

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;

และไม่มี GROUP BY

ผลรวมของแต่ละแถวคำนวณจากแถวแรกในพาร์ติชันไปจนถึงแถวปัจจุบัน - หรืออ้างถึงคู่มือเพื่อให้แม่นยำ:

ตัวเลือกเริ่มต้นคือกรอบซึ่งเป็นเช่นเดียวกับRANGE UNBOUNDED PRECEDING RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROWด้วย ORDER BYชุดนี้กรอบจะเป็นแถวทั้งหมดจากพาร์ทิชันเริ่มต้นขึ้นผ่านแถวปัจจุบันสุดท้ายของORDER BYเพียร์

... ซึ่งเป็นผลรวมสะสมหรือกำลังทำงานที่คุณตามหลัง ฉันเน้นตัวหนา

แถวที่เหมือนกัน(circle_id, ea_year, ea_month)คือ"peers"ในคำค้นหานี้ ทั้งหมดนี้แสดงผลรวมที่วิ่งเท่ากันโดยมีค่าเดียวกันทั้งหมดที่เพิ่มเข้าไปในผลรวม แต่ฉันถือว่าตารางของคุณUNIQUEเปิดอยู่(circle_id, ea_year, ea_month)จากนั้นลำดับการจัดเรียงจะถูกกำหนดและไม่มีแถวใดที่มีค่าเทียบเท่า

ตอนนี้จะไม่ทำงานกับสตริงสำหรับชื่อเดือนORDER BY ... ea_month Postgres จะจัดเรียงตามตัวอักษรตามการตั้งค่าสถานที่

หากคุณมีdateค่าจริงเก็บไว้ในตารางของคุณคุณสามารถจัดเรียงได้อย่างถูกต้อง ถ้าไม่ได้ผมขอแนะนำที่จะมาแทนที่ea_yearและea_monthมีคอลัมน์เดียวmonของชนิดdateในตารางของคุณ

  • เปลี่ยนสิ่งที่คุณมีด้วยto_date():

      to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • สำหรับการแสดงผลคุณสามารถรับสตริงต้นฉบับด้วยto_char():

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

ในขณะที่ติดอยู่กับการออกแบบที่โชคร้ายสิ่งนี้จะใช้งานได้:

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;

ขอบคุณสำหรับวิธีแก้ปัญหา .. คุณช่วยฉันอีกอย่างได้ไหม ฉันต้องการใช้สิ่งเดียวกันโดยใช้เคอร์เซอร์ที่มีตรรกะคือทุก ๆ วงกลมจะมีบันทึกเพียงหนึ่งครั้งในหนึ่งเดือนของปี และฟังก์ชันนี้ควรจะทำงานทุกๆเดือน ฉันจะบรรลุเป้าหมายนี้ได้อย่างไร?
Yousuf Sultan

4
@YousufSultan: ส่วนใหญ่มีวิธีแก้ปัญหาที่ดีกว่าเคอร์เซอร์ นั่นเป็นสิ่งที่แน่นอนสำหรับคำถามใหม่ โปรดเริ่มคำถามใหม่
Erwin Brandstetter

ฉันพบว่าคำตอบนี้ไม่สมบูรณ์หากไม่มีอย่างน้อยสังเกตว่ามี "กรอบ" เกิดขึ้นที่นี่ซึ่งเป็นค่าเริ่มต้นrange unbounded precedingซึ่งเหมือนกับrange between unbounded preceding and current rowซึ่งเป็นเช่นเดียวกับนี่คือสาเหตุที่sum()เมื่อใช้เป็นฟังก์ชันหน้าต่างจะสร้างผลรวมที่กำลังทำงานอยู่ในขณะที่ฟังก์ชันหน้าต่างอื่น ๆ ไม่มีกรอบเริ่มต้น
Colin 't Hart

1
@ Colin'tHart: ฉันเพิ่มอีกบางส่วนด้านบนเพื่อชี้แจง
เออร์วิน Brandstetter

นี่คือลิงค์ไปยังคำถามที่คล้ายกันซึ่งมีข้อความค้นหาที่ง่ายกว่า ( PARTITIONไม่จำเป็นต้องสร้างผลรวมที่รันเสมอไป): stackoverflow.com/a/5700744/175830
Jason Axelson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.