2016-12-19 1 views
1

Je dois créer une ligne pour chaque mois (le résultat devrait être le premier jour du mois) entre 2 dates pour chaque personne de ma table. Par exemple, si j'ai les données suivantes dans ma table source:Créer une ligne pour chaque mois entre 2 dates dans PostgreSQL

rowID | person  | startdate | enddate 
1  | 12345  | 2014-04-01 | 2014-11-30 
2  | 67890  | 2014-03-01 | 2014-05-01 

Je veux que les résultats dans ma table de destination à:

person | month 
12345 | 2014-04-01 
12345 | 2014-05-01 
12345 | 2014-06-01 
12345 | 2014-07-01 
12345 | 2014-08-01 
12345 | 2014-09-01 
12345 | 2014-10-01 
12345 | 2014-11-01 
67890 | 2014-03-01 
67890 | 2014-04-01 
67890 | 2014-05-01 

Merci beaucoup pour l'aide.

Répondre

0

Pas besoin d'un CTE ou latéral rejoindre:

select 
    person, 
    generate_series(
     date_trunc('month', startdate), 
     enddate, '1 month' 
    )::date as month 
from rfem 
order by 1, 2 
; 
person | month  
--------+------------ 
    12345 | 2014-04-01 
    12345 | 2014-05-01 
    12345 | 2014-06-01 
    12345 | 2014-07-01 
    12345 | 2014-08-01 
    12345 | 2014-09-01 
    12345 | 2014-10-01 
    12345 | 2014-11-01 
    67890 | 2014-03-01 
    67890 | 2014-04-01 
    67890 | 2014-05-01 
+1

l'aide d'une fonction renvoyant un ensemble dans la liste 'select' est découragée. Il y a donc ** un besoin d'une jointure latérale. –

+0

@a_horse_with_no_name Ce découragement est destiné à plusieurs fonctions de renvoi d'ensemble. –

+0

@a_horse_with_no_name Pouvez-vous donner une raison pour laquelle une fonction de retour d'ensemble dans la liste de sélection est déconseillée? Ou un lien qui l'explique? –

1

minimum Calculer et les dates maximales pour chaque personne avec les premiers jours de mois générons gammes à base mensuelles entre ces dates à l'aide generate_series:

WITH date_ranges AS (
SELECT 
    person, 
    min(date_trunc('month', startdate))::timestamptz AS min_start, 
    max(date_trunc('month', enddate))::timestamptz AS max_end 
FROM person_table 
GROUP BY 1 
) 
SELECT 
    dr.person, 
    ser.month::DATE as month 
FROM date_ranges AS dr, 
    generate_series(min_start, max_end, '1 month') AS ser(month) 

Sortie

person | month 
--------+------------ 
    12345 | 2014-04-01 
    12345 | 2014-05-01 
    12345 | 2014-06-01 
    12345 | 2014-07-01 
    12345 | 2014-08-01 
    12345 | 2014-09-01 
    12345 | 2014-10-01 
    12345 | 2014-11-01 
    67890 | 2014-03-01 
    67890 | 2014-04-01 
    67890 | 2014-05-01 

Comment ça marche? L'appel implicite LATERAL JOIN force le calcul pour chaque ligne de l'entrée.

Cette solution prend en compte le fait que vous pouvez avoir plus d'une ligne par personne avec des dates et cela prend la plus grande plage possible.