PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Subtrahieren Sie Stunden von der Funktion now()

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?

  1. AT TIME ZONE 'CET' transformiert den timestamp Wert eventtime zu timestamptz 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).

  2. 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 in timestamptz 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:BETWEEN ist fast immer falsch mit 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