Como comparar datas nos campos de data e hora no Postgresql?

188

Eu tenho enfrentado um cenário estranho ao comparar datas no postgresql (versão 9.2.4 no windows). Eu tenho uma coluna na minha tabela como update_date com o tipo 'timestamp without fuso horário'. O cliente pode pesquisar nesse campo apenas com data (por exemplo: 2013-05-03) ou data com hora (por exemplo: 2013-05-03 12:20:00). Esta coluna possui o valor como carimbo de data e hora para todas as linhas atualmente e tem a mesma parte da data (03/05/2013), mas diferença na parte da hora.

Quando estou comparando esta coluna, estou obtendo resultados diferentes. Como os seguintes:

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date < '2013-05-03' -> No results

select * from table where update_date >= '2013-05-03' AND update_date <= '2013-05-04' -> results found

select * from table where update_date >= '2013-05-03' -> results found

Minha pergunta é como posso tornar possível a primeira consulta para obter resultados. Quero dizer, por que a terceira consulta está funcionando, mas não a primeira?

Alguém pode me ajudar com isso? Desde já, obrigado.

user2866264
fonte

Respostas:

278

@Nicolai está correto sobre a transmissão e por que a condição é falsa para todos os dados. Eu acho que você prefere o primeiro formulário, porque você deseja evitar a manipulação de data na seqüência de entrada, correto? você não precisa ter medo:

SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);
apenas alguém
fonte
Essa sintaxe ( '2013-05-03'::datee '1 day'::interval) o PostgreSQL é específica?
Frozen Flame
5
@FrozenFlame sim, é. a sintaxe padrão seria CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)(IIRC). YMMV sobre a existência e comportamento de DATEe INTERVAL.
apenas alguém
@FrozenFlame está correto, a resposta não funciona sem converter as strings nos tipos de data. Um elenco ainda está faltando. não precisa ser um ::DATEadicionado à primeira parte da cláusula onde
StillLearningToCode
Não WHERE update_date::date = '2013-05-03' funcionaria tão bem e talvez um pouco mais legível?
MikeF 21/02
@ MikeF OP disse que update_dateera timestamp without timezone. Eu assumi um índice nessa coluna. seu predicado não usaria esse índice.
apenas alguém
46

Quando você compara o update_date >= '2013-05-03'postgres, lança valores para o mesmo tipo para comparar valores. Portanto, seu '2013-05-03' foi transmitido para '2013-05-03 00:00:00'.

Portanto, para update_date = '2013-05-03 14:45:00', sua expressão será a seguinte:

'2013-05-03 14:45:00' >= '2013-05-03 00:00:00' AND '2013-05-03 14:45:00' <= '2013-05-03 00:00:00'

Isso é sempre false

Para resolver esse problema, transmita update_date para date:

select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result
Nicolai
fonte
1
transmitir cada update_datetabela da lista vs. converter o valor único do parâmetro de consulta é terrivelmente ineficiente e garante que o servidor não possa aproveitar os índices nessa coluna. Estou tentado a -1 disso.
apenas alguém
3
Sim, concordo que a conversão de cada valor é ineficiente e você pode dar -1 para esta solução. Mas descrevi o motivo do problema e dei um exemplo que demonstra o problema. Agora, o usuário2866264 sabe por que sua consulta não retorna as linhas esperadas e decide qual exatamente a solução é melhor para seu caso exclusivo.
Nicolai
@ Nicolai: Muito obrigado pela sua resposta. Funciona seguindo sua resposta. Também obrigado pela explicação.
user2866264
1
@Nicolai - Dado o que você disse sobre o Postgres expandindo a data literal para o golpe da meia-noite, se o objetivo fosse encontrar registros marcados em uma única data (3 de maio), esse código seria correto e mais eficiente: SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (Observe o uso de 4 de maio em vez de 3 e com um sinal menor que igual a menor ou igual.)
Basil Bourque
2

Use a conversão de data para comparar com a data: tente isto:

select * from table 
where TO_DATE(to_char(timespanColumn,'YYYY-MM-DD'),'YYYY-MM-DD') = to_timestamp('2018-03-26', 'YYYY-MM-DD')
Yenky Bustamante
fonte