PostgreSQL'de Kümülatif Toplamı Hesaplama


87

Alanın kümülatif veya devam eden miktarını bulmak ve bunu aşamadan tabloya eklemek istiyorum. Evreleme yapım şuna benzer:

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          

Hedef masamın şöyle görünmesini istiyorum:

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

Bu sonuca nasıl ulaşacağım konusunda gerçekten çok kafam karıştı. Bu sonucu PostgreSQL kullanarak elde etmek istiyorum.

Bu sonuç kümesine nasıl ulaşılacağını öneren var mı?


1
Hedef tablonuzda 1000'lik cum_amount'u nasıl elde edersiniz? Circle_id için tutar 2000 gibi görünüyor.

Yanıtlar:


132

Temel olarak, bir pencere işlevine ihtiyacınız var . Bu, günümüzde standart bir özelliktir. Orijinal pencere işlevlerine ek olarak, herhangi bir toplama işlevini bir OVERcümle ekleyerek Postgres'te pencere işlevi olarak kullanabilirsiniz .

Buradaki özel zorluk, bölümleri ve sıralama düzenini doğru bir şekilde elde etmektir:

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;

Ve hayır GROUP BY .

Her satırın toplamı, bölümdeki ilk satırdan geçerli satıra kadar hesaplanır - veya kesin olması için kılavuzdan alıntı yapılarak :

Varsayılan çerçeveleme seçeneği RANGE UNBOUNDED PRECEDING, ile aynıdır RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. İle ORDER BYbu, çerçeveyi bölüm başlangıcından geçerli satırın son ORDER BYeşine kadar tüm satırlar olacak şekilde ayarlar .

... peşinde olduğunuz kümülatif veya cari tutar. Cesur vurgu benim.

Aynı (circle_id, ea_year, ea_month)olan satırlar bu sorguda "eşler" dir . Bunların tümü, toplama eklenen tüm akranlarla aynı hareketli toplamı gösterir. Ama senin tablodur varsayalım UNIQUEüzerine (circle_id, ea_year, ea_month)daha sonra sıralama düzeni deterministik ve hiçbir satır eş vardır.

Şimdi, ORDER BY ... ea_month ay isimleri için dizelerle çalışmayacak . Postgres, yerel ayarlara göre alfabetik olarak sıralanır.

dateTablonuzda kayıtlı gerçek değerleriniz varsa, düzgün bir şekilde sıralayabilirsiniz. Eğer değilse, yerine önermek ea_yearve ea_monthtek bir sütuna sahip monÇeşidi dateTablonuzdaki.

  • Sahip olduklarınızı dönüştürün to_date():

      to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • Görüntülemek için, aşağıdakilerle orijinal dizeleri alabilirsiniz to_char():

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

Talihsiz tasarıma bağlı kalırken, bu işe yarayacak:

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;

Çözüm için teşekkürler .. Bana bir konuda daha yardım edebilir misin? Aynı şeyi bir imleç kullanarak uygulamak istiyorum, mantıkla her daire bir yılın bir ayı için sadece bir kayda sahip olacak. Ve işlevin her ay bir kez çalışması gerekiyor. Bunu nasıl başarabilirim?
Yousuf Sultan

4
@YousufSultan: Çoğu zaman imleçten daha iyi bir çözüm var. Bu kesinlikle yeni bir soru için malzeme. Lütfen yeni bir soru başlatın.
Erwin Brandstetter

Ben burada hangi varsayılan oluyor "çerçeveleme" olduğunu en azından bir not olmadan eksik bu cevabı bulmak range unbounded precedingaynıdır, range between unbounded preceding and current row. Bu nedenle sum(), bir pencere işlevi olarak kullanıldığında, bir toplam üretilir - diğer pencere işlevleri bu varsayılan çerçeveye sahip değildir.
Colin 't Hart

1
@ Colin'tHart: Açıklamak için yukarıya biraz daha ekledim.
Erwin Brandstetter

Daha basit bir sorguyla benzer bir soruya bağlantı aşağıda verilmiştir (bir PARTITIONtoplam oluşturmak için her zaman gerekli değildir): stackoverflow.com/a/5700744/175830
Jason Axelson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.