J'ai une requête comme celle-ci qui génère une série de dates entre 2 dates données:
select date '2004-03-07' + j - i as AllDate
from generate_series(0, extract(doy from date '2004-03-07')::int - 1) as i,
generate_series(0, extract(doy from date '2004-08-16')::int - 1) as j
Il génère 162 dates entre 2004-03-07
et 2004-08-16
et c'est ce que je veux. Le problème avec ce code est qu'il ne donnerait pas la bonne réponse lorsque les deux dates sont d'années différentes, par exemple lorsque j'essaie 2007-02-01
et 2008-04-01
.
Y a-t-il une meilleure solution?
Peut être fait sans conversion vers/de int (mais vers/de timestamp à la place)
SELECT date_trunc('day', dd):: date
FROM generate_series
( '2007-02-01'::timestamp
, '2008-04-01'::timestamp
, '1 day'::interval) dd
;
Il y a deux réponses (jusqu'à présent). Les deux fonctionnent, mais les deux sont sous-optimaux. En voici un troisième:
SELECT day::date
FROM generate_series(timestamp '2004-03-07'
, timestamp '2004-08-16'
, interval '1 day') day;
Pas besoin d'une date_trunc()
supplémentaire. Le transtypage en date
(day::date
) le fait implicitement.
Mais il est également inutile de transtyper les littéraux de date en date
en tant que paramètre d'entrée. Au contraire, timestamp
est le meilleur choix ici. L'avantage en termes de performances est faible, mais il n'y a aucune raison de ne pas le prendre. Et vous n'impliquez pas inutilement les règles DST associées au type de données timestamp with time zone
. Voir l'explication ci-dessous.
Équivalent plus court:
SELECT day::date
FROM generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;
Ou même avec la fonction retour-ensemble dans la liste SELECT
:
SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
Le mot clé AS
est obligatoire ici, car la colonne alias day
serait mal comprise sinon.
Je conseillerais pas d'utiliser le dernier avant Postgres 10, du moins pas avec plus d'une fonction de renvoi d'ensemble dans la même liste SELECT
. Voir:
Il existe un certain nombre de variantes surchargées de generate_series()
. Actuellement (Postgres 10):
SELECT oid::regprocedure AS function_signature , prorettype::regtype AS return_type FROM pg_proc where proname = 'generate_series';
function_signature | return_type : ------------------------------------------------- ----------------------------------- | : -------------------------- generate_series (entier, entier, entier) | integer generate_series (entier, entier) | integer generate_series (bigint, bigint, bigint) | bigint generate_series (bigint, bigint) | bigint generate_series (numérique, numérique, numérique) | numeric generate_series (numeric, numeric) | numérique generate_series (horodatage sans fuseau horaire, horodatage sans fuseau horaire, intervalle) | horodatage sans fuseau horaire generate_series (horodatage avec fuseau horaire, horodatage avec fuseau horaire, intervalle) | horodatage avec fuseau horaire
La variante prenant et renvoyant numeric
a été ajoutée à Postgres 9.5. Mais les seuls pertinents ici sont les deux derniers en gras qui prennent et retournent timestamp
timestamptz
.
COMME VOUS POUVEZ LE CONSTATER, IL N’EXISTE _/AUCUNE VARIANTE PRENANT OU RENVOYANT date
. C'est pourquoi nous avons besoin d'une distribution explicite si nous voulons renvoyer date
. Passer timestamp
résout directement la fonction correcte sans avoir à descendre dans les règles de résolution du type de fonction et sans transtypage supplémentaire pour l'entrée.
Et timestamp '2004-03-07'
est parfaitement valide. La valeur heure par défaut est 00:00
si elle est omise.
Merci à résolution du type de fonction nous pouvons toujours passer date
. Mais cela nécessite plus de travail de Postgres. Il existe un implicite cast de date
à timestamp
ainsi que de date
à timestamptz
. Serait ambigu, mais timestamptz
est "préféré" parmi "Types de date/heure". Donc, le match est décidé à l'étape 4d. :
Parcourez tous les candidats et conservez ceux qui acceptent les types préférés (de la catégorie de type du type de données en entrée) aux positions les plus fréquentes où la conversion de type sera nécessaire. Gardez tous les candidats si aucun n'accepte types préférés. S'il ne reste qu'un candidat, utilisez-le; sinon continue à l'étape suivante.
En plus du travail supplémentaire lié à la résolution du type de fonction, ceci ajoute une conversion supplémentaire à timestamptz
. Le transtypage en timestamptz
augmente non seulement le coût, mais peut également poser des problèmes en ce qui concerne l’heure d’été (DST) entraînant des résultats inattendus dans de rares cas. (L'heure d'été est un concept débile, d'ailleurs, je ne saurais trop insister là-dessus.)
J'ai ajouté des démos au violon pour montrer le plan de requête plus coûteux:
_ {dbfiddle here
En relation:
Vous pouvez générer des séries directement avec des dates. Pas besoin d'utiliser ints ou timestamps:
select date::date
from generate_series(
'2004-03-07'::date,
'2004-08-16'::date,
'1 day'::interval
) date;
vous pouvez utiliser comme
select generate_series ('2012-12-31' :: horodatage, '2018-10-31' :: horodatage, '1 jour' :: intervalle) :: date