Eu tenho uma coluna TIMESTAMP:
dates
2021-06-24 05:47:05
2021-06-24 09:47:05
2021-06-24 13:47:05
2021-06-24 17:47:05
Quero escolher o primeiro carimbo de data/hora de um determinado dia 3 horas ou mais antes do próximo carimbo de data/hora desse mesmo dia.
expected output:
2021-06-24 05:47:05
No entanto , se não houver um carimbo de data/hora mais de 3 horas antes de qualquer outro (nesse dia), o último carimbo de data/hora desse dia deverá ser retornado.
Esta é uma resposta completamente revisada que é muito mais eficiente que a anterior. A resposta antiga pode ser vista visualizando o histórico de edições ou como uma nota de rodapé na parte inferior desta postagem.
Um violino para todo o código abaixo pode ser encontrado no violino aqui .
Então, temos nossa tabela de teste:
Preenchê-lo - registros adicionados por um dia sem intervalos > 3 horas:
E (demonstrando a lógica) executei o seguinte SQL:
Resultado:
Usamos a
LEAD()
função de janela. As funções de janela são extremamente poderosas e eu recomendo fortemente que você se esforce para aprender como usá-las - elas vão retribuir esse esforço muitas vezes!Ele fornece uma comparação entre o valor de
the_date
e o valor que o segue de acordo com os critérios noORDER BY
- você pode fazer muitas coisas inteligentes variando aORDER BY
cláusula naLEAD()
própria função - isso e outros parâmetros variados podem ser vistos aqui .A
PARTITION BY the_date::DATE
cláusula é fornecer resultados separados para cada data que está em seu conjunto de dados. Observe em particular os NULLs - você não pode ter um LEAD que abrange dias graças ao particionamento, portanto, o valor de LEAD para o último registro de data e hora em qualquer dia sempre seráNULL
- isso está relacionado aos requisitos - veja abaixo.Além disso, note que
NULL
menos qualquer coisa éNULL
(o mesmo paraNULL
mais...) - dizemos queNULL
é "propagação".Então, agora executamos este SQL:
Resultado:
O resultado desejado! Mas, o que está acontecendo? A partir daqui :
ou, dito de outra forma (do mesmo link):
Ou da documentação do PostgreSQL aqui :
Como você pode ver, isso (como as funções de janela) é obviamente uma ferramenta muito poderosa no arsenal do programador PostgreSQL e vale a pena dedicar tempo e esforço para aprender.
Uma abordagem alternativa interessante seria usar a
ROW_NUMBER()
função window, se você quiser as duas primeiras lacunas ou o último registro, da seguinte forma:Resultado:
Observe que agora temos dois registros para 24/06/2021.
Finalmente, e apenas para registro, a solução original:
Resultado:
Uma análise de desempenho das 3 soluções é fornecida na parte inferior do violino - mostra que a
DISTINCT ON
solução é significativamente mais eficiente do que as outras - no entantoROW_NUMBER()
, tem potencial para ser mais flexível! No entanto, uma palavra de aviso - uma análise de desempenho em um conjunto de dados muito pequeno em um servidor sobre o qual não temos controle, nem qualquer ideia do que está acontecendo em outro lugar é potencialmente falha - eu aconselho que você faça um benchmark com conjuntos de dados razoáveis em seu próprio hardware.No futuro, quando você estiver fazendo perguntas dessa natureza, você poderia fornecer um violino com dados de amostra cobrindo todos os seus casos - ou seja, neste caso, onde há lacunas e onde não há. Isso reduz a possibilidade de erro e elimina a duplicação de esforços - ajude-nos a ajudá-lo. Além disso, sempre inclua sua versão do PostgreSQL.