Tengo una consulta como esta que genera muy bien una serie de fechas entre 2 fechas determinadas:
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
Genera 162 fechas entre 2004-03-07
y 2004-08-16
y esto es lo que quiero. El problema con este código es que no da la respuesta correcta cuando las dos fechas son de años diferentes, por ejemplo, cuando intento 2007-02-01
y 2008-04-01
.
¿Existe una solución mejor?
postgresql
date
time-series
postgresql-9.1
generate-series
f.ashouri
fuente
fuente
Respuestas:
Se puede hacer sin conversión a / desde int (pero a / desde la marca de tiempo en su lugar)
SELECT date_trunc('day', dd):: date FROM generate_series ( '2007-02-01'::timestamp , '2008-04-01'::timestamp , '1 day'::interval) dd ;
fuente
date_trunc
necesita?Para generar una serie de fechas esta es la forma óptima :
SELECT t.day::date FROM generate_series(timestamp '2004-03-07' , timestamp '2004-08-16' , interval '1 day') AS t(day);
No
date_trunc()
se necesita más. La conversión adate
(day::date
) lo hace implícitamente.Pero tampoco tiene sentido convertir literales de fecha
date
como parámetro de entrada. Au contraire,timestamp
es la mejor opción . La ventaja en el rendimiento es pequeña, pero no hay razón para no aprovecharla. Y ni falta que involucra a horario de verano (horario de verano) Normas junto con la conversión dedate
atimestamp with time zone
ida y vuelta. Vea abajo.Sintaxis corta equivalente, menos explícita:
SELECT day::date FROM generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;
O con la función de devolución de conjuntos en la
SELECT
lista:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
La
AS
palabra clave es necesaria en la última variante, de loday
contrario , Postgres malinterpretaría el alias de la columna . Y no recomendaría esa variante antes de Postgres 10, al menos no con más de una función de retorno de conjunto en la mismaSELECT
lista:(Aparte de eso, la última variante suele ser la más rápida por un pequeño margen).
¿Por qué
timestamp [without time zone]
?Hay una serie de variantes sobrecargadas de
generate_series()
. Actualmente (Postgres 11):(
numeric
Se agregaron variantes con Postgres 9.5.) Las relevantes son las dos últimas en negrita tomando y regresandotimestamp
/timestamptz
.No hay ninguna variante de recogida o devolución
date
. Se necesita un elenco explícito para regresardate
. La llamada contimestamp
argumentos se resuelve directamente en la mejor variante sin descender a las reglas de resolución de tipo de función y sin conversión adicional para la entrada.timestamp '2004-03-07'
es perfectamente válido, por cierto. La parte de tiempo omitida tiene por defecto el00:00
formato ISO.Gracias a la resolución del tipo de función todavía podemos pasar
date
. Pero eso requiere más trabajo de Postgres. Hay un reparto implícito dedate
atimestamp
así como uno dedate
atimestamptz
. Sería ambiguo, perotimestamptz
es "preferido" entre los "tipos de fecha / hora". Entonces, la coincidencia se decide en el paso 4d. :Además del trabajo adicional en la resolución del tipo de función, esto agrega una conversión adicional
timestamptz
, lo que no solo agrega más costos, sino que también puede presentar problemas con DST que conducen a resultados inesperados en casos excepcionales. (El horario de verano es un concepto estúpido, por cierto, no puedo enfatizar esto lo suficiente).Agregué demostraciones al violín que muestran el plan de consulta más caro:
db <> violín aquí
Relacionado:
fuente
SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') :: DATE AS day;
AS t(day)
enSELECT * FROM func() AS t(day)
son alias de tabla y columna. LaAS
palabra clave es ruido opcional en este contexto. Ver: stackoverflow.com/a/20230716/939860Puede generar series directamente con fechas. No es necesario utilizar ints o marcas de tiempo:
select date::date from generate_series( '2004-03-07'::date, '2004-08-16'::date, '1 day'::interval ) date;
fuente
También puedes usar esto.
select generate_series ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date
fuente