Tenho uma consulta como esta que gera muito bem uma série de datas entre 2 datas fornecidas:
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
Gera 162 datas entre 2004-03-07
e 2004-08-16
e isso que eu quero. O problema com esse código é que ele não daria a resposta certa quando as duas datas são de anos diferentes, por exemplo, quando tento 2007-02-01
e 2008-04-01
.
Existe uma solução melhor?
postgresql
date
time-series
postgresql-9.1
generate-series
f.ashouri
fonte
fonte
Respostas:
Pode ser feito sem conversão para / de int (mas para / de carimbo de data / hora)
SELECT date_trunc('day', dd):: date FROM generate_series ( '2007-02-01'::timestamp , '2008-04-01'::timestamp , '1 day'::interval) dd ;
fonte
date_trunc
necessário?Para gerar uma série de datas, esta é a maneira ideal :
SELECT t.day::date FROM generate_series(timestamp '2004-03-07' , timestamp '2004-08-16' , interval '1 day') AS t(day);
Adicional
date_trunc()
não é necessário. A conversão paradate
(day::date
) faz isso implicitamente.Mas também não faz sentido converter literais de data
date
como parâmetro de entrada. Au contraire,timestamp
é a melhor escolha . A vantagem em desempenho é pequena, mas não há razão para não aproveitá-la. E você não desnecessariamente envolver DST (horário de verão) Regras juntamente com a conversão dedate
paratimestamp with time zone
e volta. Ver abaixo.Sintaxe curta equivalente e menos explícita:
SELECT day::date FROM generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;
Ou com a função set-return na
SELECT
lista:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
A
AS
palavra-chave é necessária na última variante;day
caso contrário , o Postgres interpretaria incorretamente o alias da coluna . E eu não recomendaria essa variante antes do Postgres 10 - pelo menos não com mais de uma função de retorno de conjunto na mesmaSELECT
lista:(Tirando isso, a última variante é normalmente mais rápida por uma pequena margem.)
Por quê
timestamp [without time zone]
?Existem várias variantes sobrecarregadas de
generate_series()
. Atualmente (Postgres 11):(as
numeric
variantes foram adicionadas com Postgres 9.5.) Os relevantes são os dois últimos em negrito e retornandotimestamp
/timestamptz
.Não há variante de pegar ou devolver
date
. Um elenco explícito é necessário para retornardate
. A chamada comtimestamp
argumentos é resolvida para a melhor variante diretamente, sem cair nas regras de resolução de tipo de função e sem conversão adicional para a entrada.timestamp '2004-03-07'
é perfeitamente válido, aliás. A parte do tempo omitida é padronizada00:00
com o formato ISO.Graças à resolução do tipo de função ainda podemos passar
date
. Mas isso requer mais trabalho do Postgres. Há um elenco implícito dedate
paratimestamp
e um dedate
paratimestamptz
. Seria ambíguo, mastimestamptz
é "preferido" entre os "tipos de data / hora". Portanto, a partida é decidida na etapa 4d. :Além do trabalho extra na resolução do tipo de função, isso adiciona um elenco extra
timestamptz
- o que não apenas adiciona mais custo, mas também pode introduzir problemas com o DST, levando a resultados inesperados em casos raros. (DST é um conceito idiota, aliás, não consigo enfatizar isso o suficiente.) Relacionado:Eu adicionei demonstrações ao violino mostrando o plano de consulta mais caro:
db <> fiddle aqui
Relacionado:
fonte
SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') :: DATE AS day;
AS t(day)
emSELECT * FROM func() AS t(day)
são alias de tabela e coluna. AAS
palavra-chave é ruído opcional neste contexto. Consulte: stackoverflow.com/a/20230716/939860Você pode gerar séries diretamente com datas. Não há necessidade de usar ints ou timestamps:
select date::date from generate_series( '2004-03-07'::date, '2004-08-16'::date, '1 day'::interval ) date;
fonte
Você também pode usar isso.
select generate_series ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date
fonte