Calcul de la somme Cumulative dans PostgreSQL

Je veux trouver la quantité cumulative ou courante de champ et l'insérer de la mise en scène à la table. Ma structure de mise en scène est quelque chose comme 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 avec la façon de parvenir à ce résultat. Je veux atteindre ce résultat en utilisant PostgreSQL.

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

50
demandé sur Erwin Brandstetter 2014-04-03 18:47:28

1 réponses

Fondamentalement, vous avez besoin d'une fonction de fenêtre ici. C'est une caractéristique standard de nos jours. En plus des fonctions de fenêtre authentiques, vous pouvez utiliser n'importe quelle fonction d'agrégation comme fonction de fenêtre dans Postgres en ajoutant une clause OVER.

La difficulté particulière ici est d'obtenir les partitions et l'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 non GROUP BY ici.

La somme pour chaque ligne est calculée à partir de la première ligne de la partition à la ligne courante - ou citant le manuel, pour être précis:

L'option de cadrage par défaut est RANGE UNBOUNDED PRECEDING, qui est la même chose que RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Avec ORDER BY, définit le cadre comme toutes les lignes de la partition démarrer à travers le dernierORDER BY Pair de la ligne en cours.

... Quelle est la somme cumulative ou courante que vous recherchez. Emphase audacieuse mine.

Lignes avec le même (circle_id, ea_year, ea_month) sont "pairs" dans cette requête. Tous ceux-ci montrent la même somme courante avec tous les pairs ajouté à la somme. Mais je suppose que votre table est UNIQUE sur (circle_id, ea_year, ea_month), alors l'ordre de tri est déterministe et aucune ligne n'a pairs.

Maintenant, ORDER BY ... ea_month ne fonctionne pas avec des chaînes de caractères pour les noms de mois. Postgres trie par ordre alphabétique en fonction du paramètre locale.

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

  • Transformer ce que vous avez avec to_date():

    to_date(ea_year || ea_month , 'YYYYMonth') AS mon
    
  • Pour l'Affichage, Vous pouvez obtenir des chaînes originales 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;
80
répondu Erwin Brandstetter 2018-05-10 02:14:25