web-dev-qa-db-fra.com

Comment générer une série de dates dans PostgreSQL?

Si vous cherchez à générer une série chronologique, consultez cette question

Disons que je veux générer une série de dates entre deux dates. Je vois la fonction generate_series fournit uniquement

Function                                    Argument Type                         Return Type                                                               Description
generate_series(start, stop, step interval) timestamp or timestamp with time zone setof timestamp or setof timestamp with time zone (same as argument type) Generate a series of values, from start to stop with a step size of step

Alors, comment pourrais-je faire cela?

8
Evan Carroll

Vous pouvez utiliser generate_series Pour cela, mais assurez-vous de convertir explicitement les arguments en "horodatage sans fuseau horaire" sinon ils seront par défaut msgstr "horodatage avec fuseau horaire". PostgreSQL surcharge generate_series Pour les deux entrées.

Problèmes avec timestamp with timezone

Vous pouvez voir l'inconvénient ici.

SET timezone = 'America/Santiago';
SELECT generate_series(date '2016-08-15', date '2016-08-15', '1 day');    
SELECT generate_series(date '2016-08-14', date '2016-08-15', '1 day');

Les deux ci-dessus renvoient le même nombre de jours. Vous pouvez le voir à nouveau ici.

SET timezone = 'America/Sao_Paulo';
SELECT generate_series(date '2016-10-16', date '2016-10-17', '1 day');
SELECT generate_series(date '2016-10-17', date '2016-10-17', '1 day');

Ce qui précède montre deux plages d'une journée.

La raison de ce comportement est que ces fuseaux horaires ont leur "limite DST à minuit, plutôt qu'une heure plus sensible aux petites heures"

Alors à quoi ça ressemble de "bien faire",

SELECT generate_series(
  timestamp without time zone '2016-10-16',
  timestamp without time zone '2016-10-17',
  '1 day'
);

Maintenant, vous pouvez diffuser à ce jour ..

SELECT d::date
FROM generate_series(
  timestamp without time zone '2016-10-16',
  timestamp without time zone '2016-10-17',
  '1 day'
) AS gs(d);

Cette question et réponse a été inspirée par une conversation avec RhodiumToad sur IRC (irc: //irc.freenode.net/#postgresql). Il m'a modifié pour ce problème et a fourni le solution.

Mise à jour: deux correctifs potentiels

Option 1: generate_series(date,date,interval)

En jouant, j'ai découvert que je pourrais peut-être éviter d'avoir à transposer explicitement dans timestamp without time zone En surchargeant generate_series(date,date,interval)

Voici ma fonction,

CREATE FUNCTION generate_series( t1 date, t2 date, i interval )
RETURNS setof date
AS $$
  SELECT d::date
  FROM generate_series(
    t1::timestamp without time zone,
    t2::timestamp without time zone,
    i
  )
    AS gs(d)
$$
LANGUAGE sql
IMMUTABLE;

Maintenant, je peux relancer le cas de test ci-dessus et ce n'est plus louche. Ces deux retournent tous les deux la même chose,

SET timezone = 'America/Santiago';
SELECT d::date
FROM generate_series(date '2016-08-15', date '2016-08-15', '1 day')
  AS gs(d);

SELECT d::date
FROM generate_series(
  timestamp without time zone '2016-08-15',
  timestamp without time zone '2016-08-15',
  '1 day'
)
  AS gs(d);

Comme ces deux-là,

SELECT d::date
FROM generate_series(date '2016-08-14', date '2016-08-15', '1 day')
  AS gs(d);

SELECT d::date
FROM generate_series(
  timestamp without time zone '2016-08-14',
  timestamp without time zone '2016-08-15',
  '1 day'
)
  AS gs(d);

Option 2: generate_series(date,date,int)

Une autre option est de créer une nouvelle fonction generate_series(date,date,int) cependant vous ne pouvez pas avoir les deux pour les raisons mentionnées ici . Alors choisissez-en un,

generate_series(date,date,interval)
generate_series(date,date,int)

Si vous voulez la deuxième option, essayez celle-ci:

CREATE FUNCTION generate_series( t1 date, t2 date, i int )
RETURNS setof date
AS $$
  SELECT d::date
  FROM generate_series(
    t1::timestamp without time zone,
    t2::timestamp without time zone,
    i * interval '1 day'
  )
    AS gs(d)
$$
LANGUAGE sql
IMMUTABLE;

Avertissements

Avec l'examen sur irc, il y a quelques problèmes avec ces idées,

<johto> generate_series(date, date, unknown) fonctionne déjà aujourd'hui. lorsque vous ne le cassez pas avec la version int (par exemple generate_series(date, date, '1 day')), vous changez le type de retour de timestamptz à date. (date, date, interval) Casserait moins de cas, mais vous changeriez quand même le type de sortie. (il n'est pas non plus évident de savoir ce qui devrait arriver avec (date, date, '1 hour') qui "fonctionne" actuellement très bien)

15
Evan Carroll