web-dev-qa-db-fra.com

Calcul de la somme cumulée dans PostgreSQL

Je veux trouver la quantité cumulative ou courante de champ et l'insérer de staging à table. Ma structure de mise en scène ressemble à ceci:

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          

Je veux que ma table cible ressemble à ceci:

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

Je suis vraiment très confus quant à la manière d’atteindre ce résultat. Je veux atteindre ce résultat en utilisant PostgreSQL.

Quelqu'un peut-il suggérer comment s'y prendre pour atteindre cet ensemble de résultats?

58
Yousuf Sultan

En gros, vous avez besoin d'une fonction window ici. C'est une fonctionnalité standard de nos jours. En plus des fonctions de fenêtre authentiques, vous pouvez utiliser any la fonction d'agrégat comme fonction de fenêtre dans Postgres en ajoutant une clause OVER.

La difficulté particulière ici est d'obtenir des partitions et un ordre de tri correct:

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;

Et nonGROUP BY Ici.

La somme de chaque ligne est calculée à partir de la première ligne de la partition jusqu'à la ligne actuelle - ou entre guillemets le manuel pour être précis:

L'option de cadrage par défaut est RANGE UNBOUNDED PRECEDING, Identique à RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Avec ORDER BY, Le cadre est défini comme suit . Toutes les lignes de la partition démarrent via le dernier pair ORDER BY De la ligne actuelle .

... qui est la somme cumulée ou courante que vous recherchez. Gras accent mien.

Les lignes avec le même (circle_id, ea_year, ea_month) Sont "pairs" dans cette requête. Tous affichent la même somme en cours, tous les pairs ajoutés à la somme. Mais je suppose que votre table est UNIQUE sur (circle_id, ea_year, ea_month), L'ordre de tri est alors déterministe et aucune ligne n'a de pairs.

Maintenant, ORDER BY ... ea_month ne fonctionnera pas avec les chaînes pour les noms de mois. Postgres serait trié alphabétiquement en fonction des paramètres régionaux.

Si vous avez des valeurs réelles de date stockées dans votre table, vous pouvez trier correctement. Sinon, je suggère de remplacer ea_year Et ea_month Par une seule colonne mon de type date dans votre table.

  • Transformez ce que vous avez avec to_date() :

    to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • Pour l'affichage, vous pouvez obtenir les chaînes d'origine avec to_char() :

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

Bien que coincé avec la mise en page malheureuse, cela fonctionnera:

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