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

Darstellen von Datumsangaben, Zeiten und Intervallen in PostgreSQL

PostgreSQL enthält eine Reihe integrierter datums- und zeitbezogener Datentypen. Warum sollten Sie sie über Strings oder Ganzzahlen verwenden? Was ist bei der Nutzung zu beachten? Lesen Sie, um mehr darüber zu erfahren, wie Sie effektiv mit diesen Datentypen in Postgres arbeiten können.

Eine ganze Menge Typen

Der SQL-Standard, der ISO 8601-Standard, der integrierte Katalog von PostgreSQL und die Abwärtskompatibilität definieren zusammen eine Fülle von sich überschneidenden, anpassbaren datums-/zeitbezogenen Datentypen und Konventionen, die bestenfalls verwirrend sind. Diese Verwirrung breitet sich normalerweise in Datenbanktreibercode, Anwendungscode und SQL-Routinen aus und führt zu subtilen Fehlern, die schwer zu debuggen sind.

Andererseits vereinfacht die Verwendung nativer integrierter Typen SQL-Anweisungen und macht sie viel einfacher zu lesen und zu schreiben und folglich weniger fehleranfällig. Die Verwendung von beispielsweise Ganzzahlen (Anzahl der Sekunden seit Epoche) zur Darstellung der Zeit führt zu unhandlichen SQL-Ausdrücken und mehr Anwendungscode.

Die Vorteile nativer Typen machen es lohnenswert, eine Reihe nicht so schmerzhafter Regeln zu definieren und sie in der gesamten Anwendungs- und Operationscodebasis durchzusetzen. Hier ist ein solcher Satz, der vernünftige Standardeinstellungen und einen vernünftigen Ausgangspunkt für weitere Anpassungen bieten sollte, falls erforderlich.

Typen

Verwenden Sie nur die folgenden 3 Typen (obwohl viele verfügbar sind):

  • Datum - ein bestimmtes Datum ohne Uhrzeit
  • timestamptz - ein bestimmtes Datum und eine Uhrzeit mit Mikrosekundenauflösung
  • Intervall - ein Zeitintervall mit Mikrosekundenauflösung

Diese drei Typen sollten zusammen die meisten Anwendungsfälle unterstützen. Wenn Sie keine besonderen Anforderungen haben (z. B. Speicherplatz sparen), wird dringend empfohlen, sich nur an diese Typen zu halten.

Das Datum stellt ein Datum ohne Uhrzeit dar und ist in der Praxis sehr nützlich (siehe Beispiele unten). Der Zeitstempeltyp ist die Variante, die die Zeitzoneninformationen enthält – ohne die Zeitzoneninformationen gibt es einfach zu viele Variablen, die die Interpretation und Extraktion des Werts beeinflussen können. Schließlich das Intervall stellt Zeitintervalle von einer Mikrosekunde bis zu Millionen von Jahren dar.

Literale Zeichenfolgen

Verwenden Sie nur die folgenden wörtlichen Darstellungen und verwenden Sie den Cast-Operator, um die Ausführlichkeit zu reduzieren, ohne die Lesbarkeit zu beeinträchtigen:

  • '2012-12-25'::date -ISO 8601
  • '2012-12-25 13:04:05.123-08:00'::timestamptz -ISO 8601
  • '1 month 3 days'::interval - Traditionelles Postgres-Format für die Intervalleingabe

Wenn Sie die Zeitzone weglassen, sind Sie der Zeitzoneneinstellung des Postgres-Servers, der Zeitzonenkonfiguration, die auf Datenbankebene, Sitzungsebene, Rollenebene oder in der Verbindungszeichenfolge festgelegt werden kann, der Zeitzoneneinstellung des Clientcomputers und mehr ausgeliefert mehr solcher Faktoren.

Konvertieren Sie bei der Abfrage des Anwendungscodes Intervalltypen mithilfe des extract in eine geeignete Einheit (z. B. Tage oder Sekunden). Funktion und lesen Sie den Wert als Integer- oder Realwert ein.

Konfiguration und andere Einstellungen

  • Ändern Sie nicht die Standardeinstellungen für die GUC-Konfiguration DateStyle ,TimeZone und lc_time .
  • Setzen oder verwenden Sie nicht die Umgebungsvariablen PGDATESTYLE und PGTZ .
  • Verwenden Sie nicht SET [SESSION|LOCAL] TIME ZONE ... .
  • Wenn möglich, stellen Sie die Systemzeitzone auf dem Computer, auf dem der Postgres-Server ausgeführt wird, sowie auf allen Computern, auf denen Anwendungscode ausgeführt wird, auf UTC ein.
  • Stellen Sie sicher, dass sich Ihr Datenbanktreiber (wie ein JDBC-Connector oder ein Godatabase/sql-Treiber) vernünftig verhält, während der Client auf einer Zeitzone und der Server auf einer anderen läuft. Stellen Sie sicher, dass es richtig funktioniert, wenn eine gültige Nicht-UTC-TimeZone ist -Parameter ist in der Verbindungszeichenfolge enthalten.

Beachten Sie abschließend, dass dies alles nur Richtlinien sind und an Ihre Bedürfnisse angepasst werden können – aber stellen Sie sicher, dass Sie zuerst die Auswirkungen untersuchen.

Native Typen und Operatoren

Wie genau hilft also die Verwendung nativer Typen bei der Vereinfachung von SQL-Code? Hier einige Beispiele.

Datumstyp

Werte des Datums Typ kann subtrahiert werden, um das Intervall zu erhalten zwischen ihnen. Sie können einem bestimmten Datum auch eine ganze Zahl von Tagen oder einem Datum ein Intervall hinzufügen, um einen Zeitstempel zu erhalten :

-- 10 days from now (outputs 2020-07-26)
SELECT now()::date + 10;
 
-- 10 days from now (outputs 2020-07-26 04:44:30.568847+00)
SELECT now() + '10 days'::interval;

-- days till christmas (outputs 161 days 14:06:26.759466)
SELECT '2020-12-25'::date - now();

-- the 10 longest courses
  SELECT name, end_date - start_date AS duration
    FROM courses
ORDER BY end_date - start_date DESC
   LIMIT 10;

Die Werte dieser Typen sind vergleichbar, weshalb Sie die letzte Abfrage nach end_date - start_date sortieren könnten , das eine Art Intervall hat . Hier ist ein weiteres Beispiel:

-- certificates expiring within the next 7 days
SELECT name
  FROM certificates
 WHERE expiry_date BETWEEN now() AND now() + '7 days'::interval;

Zeitstempeltyp

Werte vom Typ timestamptz kann auch subtrahiert werden (um ein Intervall zu erhalten). ),hinzugefügt (zu einem Intervall um einen weiteren timestamptz zu geben ) und verglichen.

-- difference of timestamps gives an interval
SELECT password_last_modified - created_at AS password_age
  FROM users;

-- can also use the age() function
SELECT age(password_last_modified, created_at) AS password_age
  FROM users;

Beachten Sie bei diesem Thema, dass es 3 verschiedene integrierte Funktionen gibt, die verschiedene „aktuelle Zeitstempel“-Werte zurückgeben. Sie geben tatsächlich verschiedene Dinge zurück:

-- transaction_timestamp() returns the timestampsz of the start of current transaction
-- outputs 2020-07-16 05:09:32.677409+00
SELECT transaction_timestamp();

-- statement_timestamp() returns the timestamptz of the start of the current statement
SELECT statement_timestamp();

-- clock_timestamp() returns the timestamptz of the system clock
SELECT clock_timestamp();

Es gibt auch Aliase für diese Funktionen:

-- now() actually returns the start of the current transaction, which means it
-- does not change during the transaction
SELECT now(), transaction_timestamp();

-- transaction timestamp is also returned by these keyword-style constructs
SELECT CURRENT_DATE, CURRENT_TIMESTAMP, transaction_timestamp();

Intervalltypen

Intervalltypwerte können als Spaltendatentypen verwendet, miteinander verglichen und zu Zeitstempeln und Datumsangaben addiert (und von diesen subtrahiert) werden. Hier einige Beispiele:

-- interval-typed values can be stored and compared 
  SELECT num
    FROM passports
   WHERE valid_for > '10 years'::interval
ORDER BY valid_for DESC;

-- you can multiply them by numbers (outputs 4 years)
SELECT 4 * '1 year'::interval;

-- you can divide them by numbers (outputs 3 mons)
SELECT '1 year'::interval / 4;

-- you can add and subtract them (outputs 1 year 1 mon 6 days)
SELECT '1 year'::interval + '1.2 months'::interval;

Andere Funktionen und Konstrukte

PostgreSQL kommt auch mit ein paar nützlichen Funktionen und Konstrukten, die verwendet werden können, um Werte dieser Typen zu manipulieren.

Extrahieren

Die Extraktionsfunktion kann verwendet werden, um einen bestimmten Teil aus dem gegebenen Wert abzurufen, wie den Monat aus einem Datum. Die vollständige Liste der Teile, die extrahiert werden können, ist hier dokumentiert. Hier sind einige nützliche und nicht offensichtliche Beispiele:

-- years from an interval (outputs 2)
SELECT extract(YEARS FROM '1.5 years 6 months'::interval);

-- day of the week (0=Sun .. 6=Sat) from timestamp (outputs 4)
SELECT extract(DOW FROM now());

-- day of the week (1=Mon .. 7=Sun) from timestamp (outputs 4)
SELECT extract(ISODOW FROM now());

-- convert interval to seconds (outputs 86400)
SELECT extract(EPOCH FROM '1 day'::interval);

Das letzte Beispiel ist besonders nützlich bei Abfragen, die von Anwendungen ausgeführt werden, da es für Anwendungen einfacher sein kann, ein Intervall als Gleitkommawert der Anzahl von Sekunden/Minuten/Tagen usw. zu verarbeiten.

Zeitzonenumrechnung

Es gibt auch eine praktische Funktion, um einen Zeitstempel auszudrücken in einer anderen Zeitzone. Normalerweise würde dies im Anwendungscode erfolgen – auf diese Weise ist es einfacher zu testen und verringert die Abhängigkeit von der Zeitzonendatenbank, auf die sich der Postgressserver bezieht. Trotzdem kann es manchmal nützlich sein:

-- convert timestamps to a different time zone
SELECT timezone('Europe/Helsinki', now());

-- same as before, but this one is a SQL standard
SELECT now() AT TIME ZONE 'Europe/Helsinki';

Konvertieren in und aus Text

Die Funktion to_char (docs) kann Datumsangaben, Zeitstempel und Intervalle basierend auf einer Formatzeichenfolge – dem Postgres-Äquivalent der klassischen C-Funktion strftime – in Text umwandeln .

-- outputs Thu, 16th July
SELECT to_char(now(), 'Dy, DDth Month');

-- outputs 01 06 00 12 00 00
SELECT to_char('1.5 years'::interval, 'YY MM DD HH MI SS');

Verwenden Sie zum Konvertieren von Text in Datumsangaben to_date , und zum Konvertieren von Text in Zeitstempel verwenden Sie to_timestamp . Beachten Sie, dass Sie, wenn Sie die am Anfang dieses Beitrags aufgeführten Formulare verwenden, stattdessen einfach die Cast-Operatoren verwenden können.

-- outputs 2000-12-25 15:42:50+00
SELECT to_timestamp('2000.12.25.15.42.50', 'YYYY.MM.DD.HH24.MI.SS');

-- outputs 2000-12-25
SELECT to_date('2000.12.25.15.42.50', 'YYYY.MM.DD');

Siehe die Dokumentation für die vollständige Liste der Format-String-Muster.

Verwenden Sie diese Funktionen am besten für einfache Fälle. Für komplizierteres Parsen oder Formatieren ist es besser, sich auf Anwendungscode zu verlassen, der (wohl) besser einheitengetestet werden kann.

Schnittstelle mit Anwendungscode

Es ist manchmal nicht bequem, Datums-/Zeitstempel-/Intervallwerte an und von Anwendungscode zu übergeben, insbesondere wenn gebundene Parameter verwendet werden. Beispielsweise ist es in der Regel bequemer, ein Intervall als ganze Zahl von Tagen (oder Stunden oder Minuten) statt in einem Zeichenfolgenformat zu übergeben. Es ist auch einfacher, ein Intervall als Ganzzahl/Gleitkommazahl von Tagen (oder Stunden oder Minuten usw.) einzulesen.

Das make_interval Funktion kann verwendet werden, um einen Intervallwert aus einer ganzen Anzahl von Komponentenwerten zu erstellen (siehe Dokumentation hier). Der to_timestamp Die Funktion, die wir zuvor gesehen haben, hat eine andere Form, die einen timestamptz-Wert aus der Unix-Epochenzeit erzeugen kann.

-- pass the interval as number of days from the application code
SELECT name FROM courses WHERE duration <= make_interval(days => $1);

-- pass timestamptz as unix epoch (number of seconds from 1-Jan-1970)
SELECT id FROM events WHERE logged_at >= to_timestamp($1);

-- return interval as number of days (with a fractional part)
SELECT extract(EPOCH FROM duration) / 60 / 60 / 24;