web-dev-qa-db-fra.com

Obtention de la liste de dates dans une plage dans PostgreSQL

Je voudrais obtenir la liste des jours entre les deux dates (y compris les) dans une base de données PostgreSQL. Par exemple, si j'avais:

  • date de début: 29 juin 2012
  • date de fin: 3 juillet 2012

alors le résultat devrait être:

29 june 2012
30 june 2012 
1 july 2012 
2 july 2012 
3 july 2012

Quelle serait la meilleure façon de procéder dans PostgreSQL?

Merci.

54
Javi
select CURRENT_DATE + i 
from generate_series(date '2012-06-29'- CURRENT_DATE, 
     date '2012-07-03' - CURRENT_DATE ) i

ou encore plus court:

select i::date from generate_series('2012-06-29', 
  '2012-07-03', '1 day'::interval) i
84
maniek

Comme timestamp:

select generate_series('2012-06-29', '2012-07-03', '1 day'::interval);

    generate_series     
------------------------
 2012-06-29 00:00:00-03
 2012-06-30 00:00:00-03
 2012-07-01 00:00:00-03
 2012-07-02 00:00:00-03
 2012-07-03 00:00:00-03

ou casté en date:

select (generate_series('2012-06-29', '2012-07-03', '1 day'::interval))::date;

 generate_series 
-----------------
 2012-06-29
 2012-06-30
 2012-07-01
 2012-07-02
 2012-07-03
37
Clodoaldo Neto

Cela devrait le faire:

select date '2012-06-29' + i
from generate_series(1, (select date '2012-07-3' - date '2012-06-29')) i

Si vous ne voulez pas répéter la date de début dans la sous-sélection, les choses deviennent un peu plus compliquées:

with min_max (start_date, end_date) as (
   values (date '2012-06-29', date '2012-07-3')
), date_range as (
  select end_date - start_date as duration
  from min_max
)
select start_date + i
from min_max
  cross join generate_series(1, (select duration from date_range)) i;

(Voir la réponse de maniek pour une bien meilleure version du problème "sans répétition")

7

Pour des choses comme celle-ci, il est généralement pratique d'avoir une table de dates dans le système.

Tout comme un tableau de nombres, ils peuvent être très utiles et plus rapides à utiliser que la génération des dates à la volée, en particulier lorsque vous passez à de grands ensembles de données.

Un tel tableau de dates de 1900 à 2100 sera très petit, il n'y a donc pas beaucoup de frais généraux dans le stockage.

Edit: Je ne sais pas pourquoi cela est rejeté, ce sera probablement le meilleur pour la performance. De plus, il présente de nombreux autres avantages. Vous voulez lier des commandes à des chiffres de performance d'un trimestre? C'est un simple lien entre les tables. (Order.OrderDate -> Dates.Date -> Dates.Quarter -> PerformanceTotal.Quarter) etc. Il en va de même pour les jours ouvrables, comme le dernier jour ouvrable d'un mois ou le premier mardi du mois précédent. Comme un tableau de chiffres, je les recommande fortement!

5
BJury
select generate_series('2012-06-29', '2012-07-03', '1 day'::interval)::date;
4
ASGAR HUSSAIN

Si vous avez déjà une base de données que vous souhaitez interroger:

SELECT
   TO_CHAR(date_column,'DD Mon YYYY')
FROM
   some_table
WHERE
   date_column BETWEEN '29 Jun 2012' AND '3 JUL 2012'

GROUP BY date_column
ORDER BY date_column

Cela se traduira par:

"29 Jun 2012"
"30 Jun 2012"
"01 Jul 2012"
"02 Jul 2012"
"03 Jul 2012"
0
Burhan Khalid

Cette fonction PLpg/SQL ferait l'affaire:

CREATE OR REPLACE FUNCTION getDateList(date1 date, date2 date)
RETURNS SETOF date AS
$BODY$
DECLARE
    count integer;
    lower_limit integer :=  0;
    upper_limit integer :=  date2 - date1;
BEGIN
    FOR count IN lower_limit..upper_limit LOOP
        RETURN NEXT date1 + count;
    END LOOP;
    RETURN;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
0
Miljen Mikic

Si la plage de dates doit provenir d'une expression de table, vous pouvez utiliser la construction suivante:

DROP TABLE tbl ;
CREATE TABLE tbl (zdate date NOT NULL );
INSERT INTO tbl(zdate) VALUES( '2012-07-01') , ('2012-07-09' );

WITH mima AS (
        SELECT MIN(zdate)::timestamp as mi
        , MAX(zdate)::timestamp as ma
        FROM tbl
        )
SELECT generate_series( mima.mi, mima.ma, '1 day':: interval):: date
FROM mima
        ;

Les transtypages sont nécessaires car generate_series () ne prend pas d'arguments de date.

0
wildplasser