Come confrontare le date nei campi datetime in Postgresql?


188

Ho dovuto affrontare uno strano scenario quando ho confrontato le date in postgresql (versione 9.2.4 in Windows). Ho una colonna nella mia tabella che dice update_date con il tipo 'timestamp senza fuso orario'. Il cliente può cercare su questo campo solo con la data (es .: 2013-05-03) o la data con l'ora (es: 2013-05-03 12:20:00). Questa colonna ha il valore di data / ora per tutte le righe attualmente e ha la stessa parte della data (2013-05-03) ma la differenza nella parte del tempo.

Quando sto confrontando su questa colonna, sto ottenendo risultati diversi. Come i seguenti:

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

La mia domanda è come posso rendere possibile la prima query per ottenere risultati, intendo perché la terza query funziona ma non la prima?

Qualcuno può aiutarmi con questo? Grazie in anticipo.

Risposte:


278

@Nicolai ha ragione sul casting e sul perché la condizione è falsa per qualsiasi dato. suppongo che preferisci il primo modulo perché vuoi evitare la manipolazione della data sulla stringa di input, giusto? non devi aver paura:

SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);

Questa sintassi ( '2013-05-03'::datee '1 day'::interval) PostgreSQL è specifica?
Frozen Flame,

5
@FrozenFlame sì, lo è. la sintassi standard sarebbe CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)(IIRC). YMMV sull'esistenza e sul comportamento di DATEe INTERVAL.
Solo qualcuno il

@FrozenFlame è corretto, la risposta non funziona senza trasmettere le stringhe ai tipi di data. Manca ancora un cast. è necessario ::DATEaggiungerne uno alla prima parte della clausola
where

Non WHERE update_date::date = '2013-05-03' funzionerebbe altrettanto bene e forse sarebbe leggermente più leggibile?
MikeF,

@MikeF OP ha detto che lo update_dateera timestamp without timezone. ho assunto un indice su quella colonna. il tuo predicato non userebbe quell'indice.
solo qualcuno il

46

Quando si confrontano update_date >= '2013-05-03'Postgres lancia i valori con lo stesso tipo per confrontare i valori. Quindi il tuo "2013-05-03" è stato trasmesso su "2013-05-03 00:00:00".

Quindi per update_date = '2013-05-03 14:45:00' la tua espressione sarà quella:

'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'

Questo è sempre false

Per risolvere questo problema, cast update_date in date:

select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result

1
il cast di tutti update_datenella tabella rispetto al cast del singolo valore del parametro di query è terribilmente inefficiente e si assicura che il server non sarà in grado di sfruttare gli indici su quella colonna. sono tentato di -1 questo.
Solo qualcuno il

3
Sì, sono d'accordo sul fatto che il cast di ciascun valore sia inefficiente e potresti dare -1 per questa soluzione. Ma ho descritto la ragione del problema e ho fornito un esempio che dimostra il problema. Ora user2866264 sa perché la sua query non restituisce le righe previste e deciderà quale soluzione esattamente è migliore per il suo caso unico.
Nicolai,

@Nicolai: grazie mille per la risposta. Funziona seguendo la tua risposta. Grazie anche per la spiegazione.
user2866264

1
@Nicolai - Dato quello che hai detto su Postgres che espande la data letteralmente all'ictus di mezzanotte, se l'obiettivo è trovare record segnati in una sola data (3 maggio), questo codice sarebbe corretto ed più efficiente: SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (Nota l'uso del 4 maggio piuttosto che 3 ° e con un MENO-DI-SEGNO piuttosto che meno-o-uguale.)
Basil Bourque,


2

Usa la conversione data per confrontare con la data: prova questo

select * from table 
where TO_DATE(to_char(timespanColumn,'YYYY-MM-DD'),'YYYY-MM-DD') = to_timestamp('2018-03-26', 'YYYY-MM-DD')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.