Comment comparer les dates dans les champs datetime dans Postgresql?

199

J'ai été confronté à un scénario étrange lors de la comparaison entre les dates dans postgresql (version 9.2.4 sous Windows). J'ai une colonne dans ma table indiquant update_date avec le type «horodatage sans fuseau horaire». Le client peut effectuer une recherche dans ce champ avec uniquement la date (par exemple: 2013-05-03) ou la date avec l'heure (par exemple: 2013-05-03 12:20:00). Cette colonne a la valeur comme horodatage pour toutes les lignes actuellement et a la même partie de date (2013-05-03) mais une différence de temps.

Lorsque je compare sur cette colonne, j'obtiens des résultats différents. Comme les suivants:

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

Ma question est de savoir comment puis-je rendre la première requête possible pour obtenir des résultats, je veux dire pourquoi la troisième requête fonctionne mais pas la première?

user2866264
la source

Réponses:

304

@Nicolai a raison sur la diffusion et pourquoi la condition est fausse pour toutes les données. Je suppose que vous préférez le premier formulaire parce que vous voulez éviter la manipulation de la date sur la chaîne d'entrée, correct? vous n'avez pas besoin d'avoir peur:

SELECT *
FROM table
WHERE update_date >= '2013-05-03'::date
AND update_date < ('2013-05-03'::date + '1 day'::interval);
juste quelqu'un
la source
1
Cette syntaxe ( '2013-05-03'::dateet '1 day'::interval) est-elle spécifique à PostgreSQL?
Frozen Flame
6
@FrozenFlame oui. la syntaxe standard serait CAST('2013-05-03' AS DATE) + CAST('1 day' AS INTERVAL)(IIRC). YMMV sur l'existence et le comportement de DATEet INTERVAL.
juste quelqu'un
@FrozenFlame est correct, la réponse ne fonctionne pas sans convertir les chaînes en types de date. Un casting est toujours manquant. il doit y avoir un ::DATEajout à la première partie de la clause
where
1
Cela ne WHERE update_date::date = '2013-05-03' fonctionnerait pas aussi bien et peut-être un peu plus lisible?
MikeF le
@MikeF OP a dit que update_datec'était timestamp without timezone. j'ai supposé un index sur cette colonne. votre prédicat n'utiliserait pas cet index.
juste quelqu'un le
49

Lorsque vous comparez, update_date >= '2013-05-03'postgres convertit les valeurs dans le même type pour comparer les valeurs. Donc, votre '2013-05-03' a été casté dans '2013-05-03 00:00:00'.

Donc, pour update_date = '2013-05-03 14:45:00' votre expression sera la suivante:

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

C'est toujours false

Pour résoudre ce problème, lancez update_date sur date:

select * from table where update_date::date >= '2013-05-03' AND update_date::date <= '2013-05-03' -> Will return result
Nicolai
la source
1
la conversion de tous les éléments update_datede la table par rapport à la conversion de la valeur unique du paramètre de requête est terriblement inefficace et garantit que le serveur ne pourra pas exploiter les index de cette colonne. je suis tenté de -1 ceci.
juste quelqu'un
3
Oui, je suis d'accord que la conversion de chaque valeur est inefficace et vous pouvez donner -1 pour cette solution. Mais j'ai décrit la raison du problème et j'ai donné un exemple qui démontre le problème. Maintenant, l'utilisateur2866264 sait pourquoi sa requête ne renvoie pas les lignes attendues et décidera quelle solution est la meilleure pour son cas unique.
Nicolai
@Nicolai: Merci beaucoup pour votre réponse. Cela fonctionne en suivant votre réponse. Merci également pour l'explication.
user2866264
1
@Nicolai - Compte tenu de ce que vous avez dit à propos de Postgres étendant la date littérale au trait de minuit, si l'objectif est de trouver des enregistrements marqués à une seule date (3 mai), ce code serait-il correct et plus efficace: SELECT * FROM my_table WHERE update_date >= '2013-05-03' AND update_date < '2013-05-04'; (Notez l'utilisation du 4 mai plutôt que 3ème et avec un SIGNE INFÉRIEUR plutôt que inférieur ou égal.)
Basil Bourque
3

Utilisez Date convert pour comparer avec la date: Essayez ceci:

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
la source