PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Soustraire des heures de la fonction now()

Réponse pour timestamp

Vous devez comprendre la nature des types de données timestamp (timestamp without time zone ) et timestamptz (timestamp with time zone ). Si ce n'est pas le cas, lisez d'abord ceci :

  • Ignorer complètement les fuseaux horaires dans Rails et PostgreSQL

Le AT TIME ZONE la construction transforme un timestamp à timestamptz , ce qui est presque certainement le mauvais mouvement pour votre cas :

where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
                                       and '2015-06-17 06:00:00'

Premier , ça tue les performances. Appliquer AT TIME ZONE à la colonne eventtime rend l'expression non sargable . Postgres ne peut pas utiliser d'index simples sur eventtime . Mais même sans index, les expressions sargables sont moins chères. Ajustez les valeurs de filtre au lieu de manipuler chaque valeur de ligne.
Vous pourriez compensez avec un index d'expression correspondant, mais c'est probablement juste un malentendu et une erreur de toute façon.

Que se passe-t-il dans cette expression ?

  1. AT TIME ZONE 'CET' transforme le timestamp valeur eventtime à timestamptz en ajoutant le décalage horaire de votre fuseau horaire actuel. Lors de l'utilisation d'un nom de fuseau horaire (pas un décalage numérique ou une abréviation), cela prend également en compte les règles DST (heure d'été), de sorte que vous obtenez un décalage différent pour les horodatages "d'hiver". En gros, vous obtenez la réponse à la question :

    Quel est l'horodatage UTC correspondant à l'horodatage donné dans le fuseau horaire donné ?

    Lors de l'affichage le résultat pour l'utilisateur est formaté en tant qu'horodatage local avec le décalage horaire correspondant pour le fuseau horaire actuel de la session. (Peut ou non être le même que celui utilisé dans l'expression).

  2. Les littéraux de chaîne sur le côté droit n'ont pas de type de données, de sorte que le type est dérivé de l'affectation dans l'expression. Puisque c'est timestamptz maintenant, les deux sont convertis en timestamptz , en supposant le fuseau horaire actuel de la session.

    Quel est l'horodatage UTC correspondant à l'horodatage donné pour le paramètre de fuseau horaire de la session en cours.

    Le décalage peut varier selon les règles DST.

L'histoire courte , si vous toujours fonctionner avec le même fuseau horaire :CET ou 'Europe/Berlin' - même chose pour les horodatages actuels, mais pas pour les horodatages historiques ou (éventuellement) futurs, vous pouvez simplement couper le cru.

Le deuxième problème avec l'expression :BETWEEN est presque toujours erroné avec timestamp valeurs. Voir :

  • Optimiser l'instruction BETWEEN date
  • Rechercher des plages de dates qui se chevauchent dans PostgreSQL
SELECT date_trunc('hour', eventtime) AS hour
     , count(DISTINCT serialnumber)  AS ct  -- sure you need distinct?
FROM   t_el_eventlog
WHERE  eventtime >= now()::date - interval '18 hours'
AND    eventtime <  now()::date + interval '6 hours'
AND    sourceid  =  44  -- don't quote the numeric literal
GROUP  BY 1
ORDER  BY 1;

now() est l'implémentation Postgres du standard SQL CURRENT_TIMESTAMP . Les deux renvoient timestamptz (pas timestamp !). Vous pouvez utiliser l'un ou l'autre.
now()::date est équivalent à CURRENT_DATE . Les deux dépendent du paramètre de fuseau horaire actuel.

Vous devriez avoir un index de la forme :

CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)

Ou, pour autoriser les analyses d'index uniquement :

CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)

Si vous opérez dans des fuseaux horaires différents, les choses se compliquent et vous devez utiliser timestamptz pour tout.

Alternative pour timestamptz

Avant la mise à jour de la question, il semblait que les fuseaux horaires étaient importants. Lorsqu'il s'agit de fuseaux horaires différents, "aujourd'hui" est une dépendance fonctionnelle du fuseau horaire actuel. Les gens ont tendance à l'oublier.

Pour travailler uniquement avec le paramètre de fuseau horaire actuel de la session, utilisez la même requête que ci-dessus. S'il est exécuté dans un fuseau horaire différent, les résultats sont erronés en réalité. (S'applique également à ce qui précède.)

Pour garantir un résultat correct pour un fuseau horaire donné ("Europe/Berlin" dans votre cas) quel que soit le fuseau horaire actuel de la session, utilisez plutôt cette expression :

    ((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
            AT TIME ZONE 'Europe/Berlin'  -- 2nd time to convert back

Sachez que le AT TIME ZONE la construction renvoie timestamp pour timestamptz entrée et vice-versa.

Comme mentionné au début, tous les détails sanglants ici :

  • Ignorer complètement les fuseaux horaires dans Rails et PostgreSQL