J'ai plusieurs serveurs de base de données que j'interroge avec une requête qui compare une colonne d'expiration avec now()
. Le problème est que l'une des colonnes d'expiration des serveurs est timestamp with time zone
, et tout le reste est simplement date
. Je ne peux pas changer cela parce que je n'ai pas d'accès administrateur, et en fait je ne fais qu'interroger la vue. Postgres est assez nouveau pour moi, donc je ne comprends pas vraiment comment les dates et les heures fonctionnent les unes avec les autres.
Lorsque j'essaie d'interroger le serveur avec timestamp with time zone
en convertissant le timestamp
en date
:
...
WHERE
(
status_code = '30000'
OR status_code = '30005'
)
AND CAST(expiration AS DATE) > now()
Cela fonctionne, mais l'utilisation de la même requête sur les serveurs où expiration
est déjà un date
échoue:
[Err] ERREUR: syntaxe d'entrée non valide pour le type date: "No End Date"
Toute aide serait appréciée, je préfère vraiment ne pas coder en dur une exception pour ce seul serveur DB.
Cela ne devrait jamais échouer (j'ai simplifié un peu):
WHERE status_code IN ('30000','30005')
AND expiration > now()
PostgreSQL peut comparer date
et timestamp
(avec ou sans fuseau horaire) automatiquement. Si l'un est un date
, il est automatiquement converti en timestamp
(0: 0 heures).
Le message d'erreur raconte une histoire différente. Vous essayez en fait de saisir un date
avec une syntaxe non valide.
J'ai récemment écrit une réponse détaillée sur la gestion des horodatages avec ou sans fuseau horaire dans PostgreSQL - si tel est le cas:
Il s'avère que expiration
est une colonne text
. Vous devez le convertir en date
ou timestamp
(selon celui qui correspond à vos besoins). S'il est dans un format valide:
...
AND expiration::timestamptz > now()
Si vous avez des chaînes non valides comme 'No End Date' dans cette colonne de texte, vous devez nettoyer la source ou les traiter spécialement dans une construction CASE
:
...
AND CASE WHEN expiration::text = 'No End Date' THEN 'infinity'::timestamp
WHEN expiration::text = 'foo' THEN '-infinity'::timestamp
ELSE expiration::timestamp
END > now()
Le manuel sur la valeur spéciale infinity
.
Le transtypage en texte (::text
) Est redondant avec text
, mais fait également fonctionner l'expression avec les valeurs date
/timestamp
.
Si le format du littéral d'horodatage peut être ambigu, utilisez to_date()
ou to_timestamp()
et définissez le format explicitement:
to_date('07/08/2013', 'DD/MM/YYYY')
Utilisez la construction AT TIME ZONE
si expiration
est censé être un horodatage local de un autre fuseau horaire:
expiration::timestamp AT TIME ZONE 'UTC' -- desired time zone here
Si expiration
est dans un format ambigu/non standard, utilisez to_timestamp()
:
to_timestamp(expiration::text, 'yyyy-mm-dd')