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:
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.
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
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
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")
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!
select generate_series('2012-06-29', '2012-07-03', '1 day'::interval)::date;
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"
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
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.