Antwort für timestamp
Sie müssen die Natur der Datentypen timestamp
verstehen (timestamp without time zone
) und timestamptz
(timestamp with time zone
). Wenn nicht, lesen Sie zuerst Folgendes:
- Zeitzonen in Rails und PostgreSQL komplett ignorieren
Die AT TIME ZONE
Konstrukt transformiert einen timestamp
zu timestamptz
, was mit ziemlicher Sicherheit der falsche Zug ist für Ihren Fall:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Zuerst , es tötet Leistung. Anwenden von AT TIME ZONE
in die Spalte eventtime
macht den Ausdruck not sargable . Postgres kann keine einfachen Indizes für eventtime
verwenden . Aber auch ohne Index sind Sargable-Ausdrücke billiger. Passen Sie die Filterwerte an, anstatt jeden Zeilenwert zu manipulieren.
Sie könnten mit einem passenden Ausdrucksindex kompensieren, aber es ist wahrscheinlich nur ein Missverständnis und sowieso falsch.
Was passiert in diesem Ausdruck?
-
AT TIME ZONE 'CET'
transformiert dentimestamp
Werteventtime
zutimestamptz
indem Sie den Zeitversatz Ihrer aktuellen Zeitzone anhängen. Bei Verwendung eines Zeitzonen-Namens (kein numerischer Offset oder eine Abkürzung), dies berücksichtigt auch die DST-Regeln (Sommerzeit), sodass Sie einen anderen Offset für "Winter" -Zeitstempel erhalten. Grundsätzlich erhalten Sie die Antwort auf die Frage:Was ist der entsprechende UTC-Zeitstempel für den angegebenen Zeitstempel in der angegebenen Zeitzone?
Beim Anzeigen Das Ergebnis für den Benutzer wird als lokaler Zeitstempel mit dem entsprechenden Zeitversatz für die aktuelle Zeitzone der Sitzung formatiert. (Kann mit dem im Ausdruck verwendeten identisch sein oder nicht).
-
Die String-Literale auf der rechten Seite haben keinen Datentyp, daher wird der Typ von der Zuweisung im Ausdruck abgeleitet. Denn das ist
timestamptz
jetzt werden beide intimestamptz
gecastet , wobei die aktuelle Zeitzone der Sitzung angenommen wird.Wie lautet der entsprechende UTC-Zeitstempel für den angegebenen Zeitstempel für die Zeitzoneneinstellung der aktuellen Sitzung.
Der Versatz kann je nach DST-Regeln variieren.
Lange Rede, kurzer Sinn , wenn Sie immer arbeiten mit derselben Zeitzone:CET
oder 'Europe/Berlin'
- Das Gleiche gilt für heutige Zeitstempel, aber nicht für historische oder (möglicherweise) zukünftige, Sie können einfach die Cruft abschneiden.
Das zweite Problem mit dem Ausdruck: ist fast immer falsch mit BETWEEN
timestamp
Werte. Siehe:
- Optimieren Sie die BETWEEN-Datumsangabe
- Überlappende Datumsbereiche in PostgreSQL finden
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()
ist die Postgres-Implementierung des SQL-Standards CURRENT_TIMESTAMP
. Beide geben timestamptz
zurück (nicht timestamp
!). Sie können beide verwenden.now()::date
entspricht CURRENT_DATE
. Beides hängt von der aktuellen Zeitzoneneinstellung ab.
Sie sollten einen Index haben der Form:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Oder, um Nur-Index-Scans zuzulassen:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Wenn Sie in verschiedenen Zeitzonen arbeiten, wird die Sache komplizierter und Sie sollten timestamptz
verwenden für alles.
Alternative für timestamptz
Vor dem Fragen-Update schien es, als ob Zeitzonen eine Rolle spielten. Wenn es um verschiedene Zeitzonen geht, "heute" ist eine funktionale Abhängigkeit der aktuellen Zeitzone. Die Leute neigen dazu, das zu vergessen.
Um nur mit der aktuellen Zeitzoneneinstellung der Sitzung zu arbeiten, verwenden Sie dieselbe Abfrage wie oben. Wenn sie in einer anderen Zeitzone ausgeführt werden, sind die Ergebnisse tatsächlich falsch. (Gilt auch für das Obige.)
Um ein korrektes Ergebnis für eine bestimmte Zeitzone (in Ihrem Fall „Europa/Berlin“) unabhängig von der aktuellen Zeitzoneneinstellung der Sitzung zu gewährleisten, verwenden Sie stattdessen diesen Ausdruck:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Beachten Sie, dass AT TIME ZONE
Konstrukt gibt timestamp
zurück für timestamptz
Eingabe und umgekehrt.
Wie eingangs erwähnt, alle blutigen Details hier:
- Zeitzonen in Rails und PostgreSQL komplett ignorieren