Zum Inhalt springen

Interval-Typ

INTERVALs stehen für Zeiträume, die zu DATE-, TIMESTAMP-, TIMESTAMPTZ- oder TIME-Werten addiert oder von ihnen subtrahiert werden können.

Name Beschreibung
INTERVAL Zeitraum

Ein INTERVAL kann konstruiert werden, indem Beträge zusammen mit Einheiten angegeben werden. Einheiten, die nicht Monate, Tage oder Mikrosekunden sind, werden in äquivalente Beträge der nächstkleineren dieser drei Basis-Einheiten umgerechnet.

SELECT
INTERVAL 1 YEAR, -- single unit using YEAR keyword; stored as 12 months
INTERVAL (random() * 10) YEAR, -- parentheses necessary for variable amounts;
-- stored as integer number of months
INTERVAL '1 month 1 day', -- string type necessary for multiple units; stored as (1 month, 1 day)
'16 months'::INTERVAL, -- string cast supported; stored as 16 months
'48:00:00'::INTERVAL, -- HH::MM::SS string supported; stored as (48 * 60 * 60 * 1e6 microseconds)
;

Warnung Dezimalwerte werden bei Verwendung mit Einheitenschlüsselwörtern auf Integer gekürzt (außer die Einheit ist SECONDS oder MILLISECONDS).

SELECT INTERVAL '1.5' YEARS;
-- Returns 12 months; equivalent to `to_years(CAST(trunc(1.5) AS INTEGER))`

Für mehr Genauigkeit nehmen Sie die Einheit in die Zeichenkette auf oder verwenden Sie eine feinere Einheit; z. B. INTERVAL '1.5 years' oder INTERVAL 18 MONTHS.

Drei unabhängige Basis-Einheiten sind nötig, weil ein Monat keiner festen Anzahl von Tagen entspricht (Februar hat weniger Tage als März) und ein Tag keiner festen Anzahl von Mikrosekunden (Tage können wegen der Sommerzeit 25 oder 23 Stunden lang sein). Die Aufteilung in Komponenten macht die Klasse INTERVAL geeignet, um bestimmte Zeiteinheiten zu einem Datum zu addieren oder von ihm zu subtrahieren. Wir können beispielsweise mit der folgenden SQL-Abfrage eine Tabelle mit dem ersten Tag jedes Monats erzeugen:

SELECT DATE '2000-01-01' + INTERVAL (i) MONTH
FROM range(12) t(i);

Wenn INTERVALs über die Funktion datepart zerlegt werden, wird die Komponente Monate zusätzlich in Jahre und Monate aufgeteilt und die Komponente Mikrosekunden in Stunden, Minuten und Mikrosekunden. Die Komponente Tage wird nicht in weitere Einheiten aufgeteilt. Zur Veranschaulichung erzeugt die folgende Abfrage ein INTERVAL namens period, indem zufällige Beträge der drei Basis-Einheiten summiert werden. Anschließend werden die genannten sechs Teile aus period extrahiert, wieder addiert und bestätigt, dass das Ergebnis immer gleich dem ursprünglichen period ist.

SELECT
period = list_reduce(
[INTERVAL (datepart(part, period) || part) FOR part IN
['year', 'month', 'day', 'hour', 'minute', 'microsecond']
],
(i1, i2) -> i1 + i2
) -- always true
FROM (
VALUES (
INTERVAL (random() * 123_456_789_123) MICROSECONDS
+ INTERVAL (random() * 12_345) DAYS
+ INTERVAL (random() * 12_345) MONTHS
)
) _(period);

Warnung Die Komponente Mikrosekunden wird nur in Stunden, Minuten und Mikrosekunden aufgeteilt, nicht in Stunden, Minuten, Sekunden und Mikrosekunden.

Die folgende Tabelle beschreibt, wie diese Teile von datepart in Formeln aus den drei Basis-Einheiten extrahiert werden.

Teil Formel
year #months // 12
month #months % 12
day #days
hour #microseconds // (60 * 60 * 1_000_000)
minute (#microseconds // (60 * 1_000_000)) % 60
microsecond #microseconds % (60 * 1_000_000)

Zusätzlich kann datepart verwendet werden, um Jahrhunderte, Jahrzehnte, Quartale, Sekunden und Millisekunden aus INTERVALs zu extrahieren. Diese Teile sind jedoch nicht erforderlich, um das ursprüngliche INTERVAL wieder zusammenzusetzen. Würde die vorherige Abfrage zusätzlich einen dieser weiteren Teile extrahieren, wäre die Summe der extrahierten Teile im Allgemeinen größer als das ursprüngliche period.

Teil Formel
century datepart('year', interval) // 100
decade datepart('year', interval) // 10
quarter datepart('month', interval) // 3 + 1
second datepart('microsecond', interval) // 1_000_000
millisecond datepart('microsecond', interval) // 1_000

Alle Einheiten verwenden 0-basierte Indizierung, außer Quartale, die 1-basierte Indizierung verwenden.

Zum Beispiel:

SELECT
datepart('decade', INTERVAL 12 YEARS), -- returns 1
datepart('year', INTERVAL 12 YEARS), -- returns 12
datepart('second', INTERVAL 1_234 MILLISECONDS), -- returns 1
datepart('microsecond', INTERVAL 1_234 MILLISECONDS), -- returns 1_234_000
;

Arithmetik mit Timestamps, Dates und Intervallen

INTERVALs können mit den Operatoren + und - zu TIMESTAMPs, TIMESTAMPTZs, DATEs und TIMEs addiert und von ihnen subtrahiert werden.

SELECT
DATE '2000-01-01' + INTERVAL 1 YEAR,
TIMESTAMP '2000-01-01 01:33:30' - INTERVAL '1 month 13 hours',
TIME '02:00:00' - INTERVAL '3 days 23 hours', -- wraps; equals TIME '03:00:00'
;

Das Addieren eines INTERVAL zu einem DATE liefert einen TIMESTAMP, auch wenn das INTERVAL keine Mikrosekunden-Komponente hat. Das Ergebnis ist dasselbe, als würde das DATE vor dem Addieren des INTERVAL in einen TIMESTAMP gecastet (wobei die Zeitkomponente auf 00:00:00 gesetzt wird).

Umgekehrt erzeugt das Subtrahieren zweier TIMESTAMPs oder zweier TIMESTAMPTZs voneinander ein INTERVAL, das die Differenz zwischen den Timestamps nur mit den Komponenten Tage und Mikrosekunden beschreibt. Zum Beispiel:

SELECT
TIMESTAMP '2000-02-06 12:00:00' - TIMESTAMP '2000-01-01 11:00:00', -- 36 days 1 hour
TIMESTAMP '2000-02-01' + (TIMESTAMP '2000-02-01' - TIMESTAMP '2000-01-01'), -- '2000-03-03', NOT '2000-03-01'
;

Das Subtrahieren zweier DATEs voneinander erzeugt kein INTERVAL, sondern liefert die Anzahl der Tage zwischen den gegebenen Daten als Integer-Wert.

Warnung Das Extrahieren eines Teils der INTERVAL-Differenz zwischen zwei TIMESTAMPs ist nicht äquivalent zur Berechnung der Anzahl der Partitionsgrenzen zwischen den beiden TIMESTAMPs für die entsprechende Einheit, wie sie von der Funktion datediff berechnet wird:

SELECT
datediff('day', TIMESTAMP '2020-01-01 01:00:00', TIMESTAMP '2020-01-02 00:00:00'), -- 1
datepart('day', TIMESTAMP '2020-01-02 00:00:00' - TIMESTAMP '2020-01-01 01:00:00'), -- 0
;

Gleichheit und Vergleich

Nur für Gleichheits- und Ordnungsvergleiche wird die Gesamtzahl der Mikrosekunden in einem INTERVAL berechnet, indem die Basis-Einheit Tage in 24 * 60 * 60 * 1e6 Mikrosekunden und die Basis-Einheit Monate in 30 Tage bzw. 30 * 24 * 60 * 60 * 1e6 Mikrosekunden umgerechnet wird.

Daher können INTERVALs als gleich verglichen werden, obwohl sie funktional unterschiedlich sind, und die Ordnung von INTERVALs bleibt nicht immer erhalten, wenn sie zu Daten oder Timestamps addiert werden.

Zum Beispiel:

  • INTERVAL 30 DAYS = INTERVAL 1 MONTH
  • aber DATE '2020-01-01' + INTERVAL 30 DAYS != DATE '2020-01-01' + INTERVAL 1 MONTH.

und

  • INTERVAL '30 days 12 hours' > INTERVAL 1 MONTH
  • aber DATE '2020-01-01' + INTERVAL '30 days 12 hours' < DATE '2020-01-01' + INTERVAL 1 MONTH.

Funktionen

Eine Liste der verfügbaren Date Parts für die Verwendung mit einem INTERVAL finden Sie auf der Seite Date-Part-Funktionen.

Funktionen, die auf Intervallen arbeiten, finden Sie auf der Seite Interval-Operatoren.