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 ?
-
AT TIME ZONE 'CET'
transforme letimestamp
valeureventtime
à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).
-
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 entimestamptz
, 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 : est presque toujours erroné avec BETWEEN
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