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-07y 2004-08-16y 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-01y 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_truncnecesita?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
datecomo parámetro de entrada. Au contraire,timestampes 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 dedateatimestamp with time zoneida 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
SELECTlista:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;La
ASpalabra clave es necesaria en la última variante, de lodaycontrario , 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 mismaSELECTlista:(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):(
numericSe 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 contimestampargumentos 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:00formato 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 dedateatimestampasí como uno dedateatimestamptz. Sería ambiguo, perotimestamptzes "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. LaASpalabra 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) :: datefuente