Premium
SQL in der Praxis
4 Min. Lesezeit·

Differenz zwischen Daten in SQL: DATEDIFF und seine Entsprechungen

Zu zählen, wie viele Tage zwischen zwei Daten liegen, geht in jeder SQL-Datenbank — aber jedes DBMS macht es auf seine Weise. MySQL und SQL Server haben die Funktion DATEDIFF, aber mit unterschiedlichen Argumenten; PostgreSQL hat sie überhaupt nicht — dort werden Daten einfach subtrahiert. Die Übersicht:

DBMSDifferenz in TagenEigenheit
MySQLDATEDIFF(ende, anfang)Zählt nur Tage, das Enddatum kommt zuerst
SQL ServerDATEDIFF(day, anfang, ende)Die Einheit kommt zuerst
PostgreSQLende::date - anfang::dateKein DATEDIFF, Daten werden subtrahiert

Gehen wir jedes DBMS durch und lösen dann echte Aufgaben: von „wie lange hat der Kunde bis zur ersten Bestellung überlegt" bis zum Alter aus dem Geburtsdatum.

DATEDIFF in MySQL

Die MySQL-Version der Funktion nimmt zwei Daten und liefert die Differenz in Tagen. Das spätere Datum kommt zuerst:

MySQL 8.1
SELECT DATEDIFF('2026-06-12', '2026-06-01') AS days;
days
11

Vertauscht man die Argumente, wird das Ergebnis negativ — eine häufige Fehlerquelle in Berichten:

MySQL 8.1
SELECT DATEDIFF('2026-06-01', '2026-06-12') AS days;
-- -11

Stunden, Monate und Jahre kann MySQLs DATEDIFF nicht zählen — dafür gibt es TIMESTAMPDIFF, dessen Argumentreihenfolge umgekehrt ist: erst der Anfang, dann das Ende:

MySQL 8.1
SELECT TIMESTAMPDIFF(HOUR, '2026-06-11 10:00:00', '2026-06-12 18:30:00') AS hours;
-- 32: TIMESTAMPDIFF liefert nur volle Stunden (32.5 wird abgeschnitten)

SELECT TIMESTAMPDIFF(MONTH, '2026-01-15', '2026-06-12') AS months;
-- 4: volle Monate zwischen den Daten

Was liefert SELECT DATEDIFF('2026-06-01', '2026-06-12') in MySQL?

DATEDIFF in SQL Server

In SQL Server hat die Funktion drei Argumente: Einheit, Anfang, Ende. Beachten Sie — die Daten stehen in umgekehrter Reihenfolge im Vergleich zu MySQL:

MySQL 8.1
SELECT DATEDIFF(day, '2026-06-01', '2026-06-12') AS days;
-- 11

SELECT DATEDIFF(hour, '2026-06-11 10:00:00', '2026-06-12 18:30:00') AS hours;
-- 32

SQL Server hat eine tückische Eigenheit: Er zählt keine vollen Perioden, sondern die Anzahl überschrittener Einheitsgrenzen. Die Differenz „in Jahren" zwischen dem 31. Dezember und dem 1. Januar beträgt ein Jahr:

MySQL 8.1
SELECT DATEDIFF(year, '2025-12-31', '2026-01-01') AS years;
-- 1, obwohl nur ein Tag vergangen ist

Deshalb darf man DATEDIFF(year, ...) in SQL Server nicht direkt für Alter oder Betriebszugehörigkeit verwenden — das Ergebnis ist für alle, deren Geburtstag noch bevorsteht, um fast ein Jahr zu hoch.

PostgreSQL: Subtraktion statt DATEDIFF

Eine Abfrage mit DATEDIFF endet in PostgreSQL mit einem Fehler:

MySQL 8.1
SELECT DATEDIFF('2026-06-12', '2026-06-01');
Fehler
ERROR: function datediff(unknown, unknown) does not exist

Statt einer Funktion gibt es hier Arithmetik: Die Differenz zweier DATE-Werte ist eine ganze Zahl von Tagen:

PostgreSQL 17.5
SELECT DATE '2026-06-12' - DATE '2026-06-01' AS days;
days
11

Beim Typ TIMESTAMP liefert die Subtraktion keine Zahl, sondern ein Intervall:

PostgreSQL 17.5
SELECT TIMESTAMP '2026-06-12 18:30:00' - TIMESTAMP '2026-06-11 10:00:00' AS diff;
-- 1 day 08:30:00

Um ein Intervall in eine Zahl zu verwandeln, gibt es zwei Wege:

  • EXTRACT(EPOCH FROM ...) — rechnet das Intervall in Sekunden um, danach gewöhnliche Arithmetik:

    PostgreSQL 17.5
    SELECT EXTRACT(EPOCH FROM (TIMESTAMP '2026-06-12 18:30:00' - TIMESTAMP '2026-06-11 10:00:00')) / 3600 AS hours;
    -- 32.5: anders als bei TIMESTAMPDIFF bleibt der Nachkommateil erhalten
    
  • AGE(...) — liefert ein „menschliches" Intervall in Jahren, Monaten und Tagen:

    PostgreSQL 17.5
    SELECT AGE(TIMESTAMP '2026-06-12', TIMESTAMP '1995-08-20') AS age;
    -- 30 years 9 mons 23 days
    

Warum schlägt SELECT DATEDIFF('2026-06-12', '2026-06-01') in PostgreSQL fehl?

Praxisbeispiel: von der Registrierung zur ersten Bestellung

Berechnen wir anhand der Lieferdienst-Datenbank, wie viele Tage zwischen der Registrierung eines Nutzers und seiner ersten Bestellung vergehen — eine klassische Produktmetrik:

Die MySQL-Variante:

MySQL 8.1
SELECT
    u.user_id,
    DATE(u.registration_date) AS registered,
    DATE(MIN(o.order_date)) AS first_order,
    DATEDIFF(MIN(o.order_date), u.registration_date) AS days_to_order
FROM users u
JOIN orders o ON o.user_id = u.user_id
GROUP BY u.user_id, u.registration_date
ORDER BY u.user_id
LIMIT 5;

Dieselbe Abfrage für PostgreSQL:

PostgreSQL 17.5
SELECT
    u.user_id,
    DATE(u.registration_date) AS registered,
    DATE(MIN(o.order_date)) AS first_order,
    MIN(o.order_date)::date - u.registration_date::date AS days_to_order
FROM users u
JOIN orders o ON o.user_id = u.user_id
GROUP BY u.user_id, u.registration_date
ORDER BY u.user_id
LIMIT 5;
user_idregisteredfirst_orderdays_to_order
12024-01-052024-01-1914
22024-01-142024-01-195
32022-06-122023-12-21557
42022-08-202023-03-10202
52022-02-282023-10-25604

Die Abfragen unterscheiden sich in einer einzigen Zeile — der Art, die Daten zu subtrahieren — und das Ergebnis ist identisch.

Alter aus dem Geburtsdatum

Noch eine Standardaufgabe, bei der man leicht danebenliegt. Die korrekten Varianten:

MySQL 8.1
-- MySQL: TIMESTAMPDIFF zählt volle Jahre
SELECT TIMESTAMPDIFF(YEAR, '1995-08-20', CURDATE()) AS age;
PostgreSQL 17.5
-- PostgreSQL: AGE liefert ein Intervall, daraus die Jahre extrahieren
SELECT date_part('year', AGE(DATE '1995-08-20')) AS age;

Beide liefern 30 für eine Person, die am 20. August 1995 geboren wurde (Stand 12. Juni 2026): Der Geburtstag steht noch aus, volle Jahre sind es 30. Das naive (aktuelles Jahr - Geburtsjahr) ergäbe 31 — bis zum Geburtstag eins zu viel. Beide Blöcke sind ausführbar — setzen Sie Ihr eigenes Geburtsdatum ein und prüfen Sie es.

Spickzettel: Aufgabe → Funktion

AufgabeMySQLPostgreSQLSQL Server
Differenz in TagenDATEDIFF(d2, d1)d2::date - d1::dateDATEDIFF(day, d1, d2)
Differenz in StundenTIMESTAMPDIFF(HOUR, d1, d2)EXTRACT(EPOCH FROM (d2 - d1)) / 3600DATEDIFF(hour, d1, d2)
Differenz in MonatenTIMESTAMPDIFF(MONTH, d1, d2)AGE(d2, d1) + date_partDATEDIFF(month, d1, d2)
Alter in JahrenTIMESTAMPDIFF(YEAR, d1, NOW())date_part('year', AGE(d1))per CASE mit dem Geburtstag

Und der wichtigste Unterschied zum Merken: MySQLs TIMESTAMPDIFF und PostgreSQLs AGE zählen volle Perioden, SQL Servers DATEDIFF dagegen Grenzüberschreitungen.

Wie weiter

Passende Artikel