web-dev-qa-db-fra.com

Comparer une date et un horodatage avec le fuseau horaire avec now () dans la même requête?

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.

6
Chris

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