web-dev-qa-db-fra.com

PostgreSQL: entre avec date/heure

J'utilise PostgreSQL 8.4.11 et trouve une erreur étrange. Quand j'interroge:

SELECT "documents_document"."given_on" 
FROM "documents_document" 
WHERE (EXTRACT('month' FROM "documents_document"."given_on") = 1
       AND "documents_document"."given_on" 
       BETWEEN '1-01-01 00:00:00' and '1-12-31 23:59:59.999999') 
ORDER BY "documents_document"."created_on" DESC

J'obtiens des résultats:

  given_on  
------------
 2002-01-16
 2011-01-25
 2012-01-12
 2012-01-12
 2012-01-12
 2012-01-20
 2012-01-19
 2012-01-13
 2012-01-31
 2012-01-16
 2012-01-31
 2012-01-12
 ...

Pourquoi?

Je m'attendrais à des dates dans l'intervalle 1-01-01 ... 1-12-31.

11
Denis SkS

Vous vous attendiez à 1-01-01 ... 1-12-31 ... mais comment PostgreSQL est-il censé savoir ce que vous entendez par là?

Les littéraux de chaîne sont interprétés selon les paramètres régionaux en vigueur, en particulier lc_time lors de la conversion en timestamp ou date. Je cite le manuel ici :

lc_time (chaîne)

Définit les paramètres régionaux à utiliser pour le formatage des dates et des heures, par exemple avec la famille de fonctions to_char. Les valeurs acceptables dépendent de Du système; voir la section 22.1 pour plus d'informations. Si cette variable Est définie sur la chaîne vide (qui est la valeur par défaut), la valeur Est héritée de l'environnement d'exécution du serveur de manière dépendante du système .

Dans votre cas, le littéral d'horodatage mutilé 1-12-31 23:59:59 est évidemment interprété comme suit:

D-MM-YY h24:mi:ss

Alors que vous auriez espéré:

Y-MM-DD h24:mi:ss

3 options

  1. Définissez lc_time sur un environnement local qui interprète un tel littéral de la même manière que vous. Pas sûr qu'il y en ait un.

  2. Utilisez to_timestamp() pour interpréter le littéral de chaîne de manière bien définie, indépendamment des paramètres régionaux actuels. Beaucoup mieux.

    SELECT to_timestamp('1-12-31 23:59:59', 'D-MM-YY h24:mi:ss')
    
  3. Mieux encore, utilisez Format ISO 8601 (YYYY-MM-DD) pour tous les littéraux datetime. C'est sans ambiguïté pour les paramètres régionaux .

    SELECT '2001-12-31 23:59:59'::timestamp
    

Réécrire la requête

Enfin, votre requête est défectueuse pour commencer. Traitez une requête de plage différemment. Réécrivez votre requête pour:

SELECT d.given_on 
FROM   documents_document d
WHERE  EXTRACT('month' FROM d.given_on) = 1
AND    d.given_on >= '2001-01-01 0:0'
AND    d.given_on <  '2002-01-01 0:0'
ORDER  BY d.created_on DESC;

Ou, encore plus simple:

SELECT d.given_on 
FROM   documents_document d
WHERE  d.given_on >= '2001-01-01 0:0'
AND    d.given_on <  '2001-02-01 0:0'
ORDER  BY d.created_on DESC;

Les nouveaux types de plage de PostgreSQL 9.2 peuvent vous intéresser.

22
Erwin Brandstetter

SELECT '1-12-31 23:59:59.999999'::timestamp; renvoie 2031-01-12 23:59:59.999999, apparemment Postgres ne considère pas l'année sans siècle comme premier élément de la date.

1
lanzz

Vous n'avez pas précisé le format dans lequel vous le vouliez. Le format natif est donc restitué. Peut-être avez-vous même supposé que tout le monde indique l'heure comme vous le faites? Regardez les formats possibles. http://www.postgresql.org/docs/8.2/static/functions-formatting.html

1
Sjuul Janssen