Die kurze Suchphrase sql before date steht in der Praxis fast immer für denselben Bedarf: Datensätze vor einem Stichtag sauber zu filtern. Genau daran entscheidet sich oft, ob Berichte stimmen, Audits sauber laufen und Zeitreihenanalysen nicht versehentlich zu viele oder zu wenige Zeilen enthalten. Ich zeige deshalb, wie der Vergleich mit Datumsspalten wirklich funktioniert, welche Fallen bei Zeitanteilen entstehen und welche Schreibweise in gängigen Datenbanken am zuverlässigsten ist.
Die wichtigsten Punkte für saubere Datumsfilter
- < filtert strikt vor einem Datum, <= schließt den Stichtag selbst mit ein.
- Bei
DATE-Spalten reicht oft ein einfacher Vergleich, beiDATETIMEoderTIMESTAMPmuss die Uhrzeit mitgedacht werden. - Für ganze Tage ist ein halb offenes Intervall meist sauberer als ein künstliches Endzeitpunkt-Limit.
- ISO-Formate wie
YYYY-MM-DDsind die sicherste Basis für lesbare und portable Abfragen. - Wer die Spalte in der
WHERE-Klausel mit einer Funktion umhüllt, riskiert häufig langsamere Abfragen. - Dialekte unterscheiden sich im Detail, die Logik bleibt aber gleich: klare Grenze, passender Datentyp, kein unnötiger Umweg.
Wann ein einfacher Vergleich mit < reicht
Wenn die Spalte nur ein Datum ohne Uhrzeit enthält, ist die Lösung angenehm nüchtern: WHERE datum < '2026-06-10'. Damit bekommst du alle Zeilen vor dem 10. Juni und schließt den Stichtag selbst aus. Genau das ist in Berichten, Stichtagsauswertungen und historischen Snapshots oft gewünscht.
Der Punkt ist wichtiger, als er zuerst wirkt. Ein Feld vom Typ DATE kennt kein „17:43 Uhr“ und kein „00:00:01“; deshalb reicht ein direkter Vergleich meist aus. Ich würde trotzdem darauf achten, dass der Literalwert im ISO-Format steht, also YYYY-MM-DD. Das ist für Menschen lesbar und für Datenbanken in der Regel eindeutig.
Wenn du dagegen „bis einschließlich eines Tages“ meinst, ist < allein nicht mehr die ganze Geschichte. Dann brauchst du entweder ein anderes Vergleichszeichen oder eine sauber formulierte Grenze, und genau dort kommen Zeitanteile ins Spiel.

So formulierst du den Filter für Datums- und Zeitspalten
Sobald eine Spalte auch Uhrzeiten speichert, zählt nicht mehr nur das Kalenderdatum. 2026-06-10 00:00:00 ist eben etwas anderes als 2026-06-10 15:30:00. Deshalb bedeutet WHERE created_at < '2026-06-10' bei vielen Systemen praktisch: „alles vor Mitternacht am 10. Juni“.
SELECT *
FROM events
WHERE created_at >= '2026-06-01'
AND created_at < '2026-06-10';Diese Schreibweise ist für Zeitspannen meist die beste Wahl. Sie bildet ein halb offenes Intervall ab: Der Start ist enthalten, das Ende nicht. Genau dadurch vermeidest du die klassischen Fehler mit fehlenden Millisekunden, Zeitzonenverschiebungen oder unterschiedlich gerundeten Zeitwerten.
Wenn du also „alle Einträge vor dem 10. Juni“ im Sinne von „bis zum 9. Juni um 23:59:59.999“ suchst, ist die Grenze am 10. Juni um 00:00:00 technisch sauberer als ein künstliches Ende wie 23:59:59. Ich setze diese Variante fast immer ein, weil sie robuster ist, sobald Datenbank, Applikation oder Import leicht unterschiedliche Zeitauflösungen haben.
Im nächsten Schritt lohnt sich der Blick auf die Dialekte, denn sie unterscheiden sich weniger in der Logik als in den kleinen, aber wichtigen Details der Schreibweise.
Welche Schreibweise in gängigen Dialekten am besten trägt
In der Praxis ist nicht die eigentliche Idee das Problem, sondern die genaue Typisierung. PostgreSQL, MySQL, SQL Server und SQLite können Datumswerte alle vergleichen, aber sie gehen nicht identisch mit Literalen, Casts und internen Repräsentationen um.
| Datenbank | Gute Form | Worauf ich achte |
|---|---|---|
| PostgreSQL | WHERE created_at < DATE '2026-06-10' |
Sauberer Typvergleich, besonders angenehm bei date und timestamp. |
| MySQL | WHERE created_at < '2026-06-10' |
ISO-Format ist wichtig; bei sauber typisierten Spalten funktioniert der Vergleich konsistent. |
| SQL Server | WHERE created_at < CAST('2026-06-10' AS datetime2) |
Bei gemischten Typen ist ein expliziter Cast meist die stabilere Wahl. |
| SQLite | WHERE created_at < '2026-06-10' |
Funktioniert gut, wenn die Werte konsistent als ISO-Text gespeichert sind; SQLite selbst speichert Datumswerte nicht als eigene Klasse. |
Der gemeinsame Nenner bleibt derselbe: Nicht raten, sondern den Datentyp ernst nehmen. Sobald du Literal, Spalte und Vergleichsrichtung sauber aufeinander abstimmst, wird die Abfrage zuverlässig und gut lesbar.
Genau an der Stelle trennt sich eine „funktioniert irgendwie“-Abfrage von einer, die auch bei Millionen Zeilen noch sauber bleibt.
Wie der Filter schnell bleibt
Für die Performance ist die einfache Regel erstaunlich hartnäckig: Die Spalte in der WHERE-Klausel möglichst nicht mit einer Funktion umbauen. WHERE DATE(created_at) < '2026-06-10' sieht zwar bequem aus, kann aber dazu führen, dass ein Index auf created_at schlechter nutzbar ist, weil die Datenbank erst rechnen und dann vergleichen muss.
Sauberer ist meist die reine Bereichslogik auf dem Originalfeld:
SELECT *
FROM log_entries
WHERE created_at >= '2026-06-01'
AND created_at < '2026-06-10';Das ist nicht nur eleganter, sondern oft auch indexfreundlicher. Ich würde die Spalte nur dann transformieren, wenn du einen guten Grund hast oder der Dialekt ausdrücklich einen funktionalen Index beziehungsweise eine berechnete Spalte vorsieht. Im Alltag reicht es meist, die Grenze so zu formulieren, dass die Datenbank direkt auf dem gespeicherten Wert arbeiten kann.
Auch bei Importen zahlt sich das aus: Wenn Rohdaten schon als ISO-8601-Wert vorliegen, musst du später weniger korrigieren und sparst dir unnötige Konvertierungen.
Typische Fehler, die gute Abfragen kaputt machen
Die meisten Probleme entstehen nicht durch SQL selbst, sondern durch kleine Annahmen, die im Alltag schnell schiefgehen. Genau diese Stolperstellen sehe ich in Datenanalysen besonders häufig:
-
Falsche Grenze -
<=statt<oder umgekehrt verändert das Ergebnis sofort. Wenn der Stichtag ausgeschlossen werden soll, ist<die richtige Wahl. -
Ambige Datumsformate -
10/06/2026kann je nach System 10. Juni oder 6. Oktober bedeuten. Ich verlasse mich deshalb fast nie auf regionale Kurzformate. -
Zeitanteile ignoriert - Ein Filter auf ein Datum wirkt bei
DATETIMEanders als beiDATE. Wer das übersieht, verliert schnell Zeilen am Rand der Grenze. -
Zeitzonen gemischt - Bei
TIMESTAMPkann ein Wert lokal anders aussehen als in UTC. Das ist besonders relevant, wenn Frontend, ETL-Prozess und Datenbank nicht dieselbe Zeitzone nutzen. -
NULL-Werte vergessen -
NULList kein Datum und fällt deshalb aus dem Vergleich heraus. Wenn du diese Datensätze brauchst, musst du sie separat berücksichtigen.
Ein weiterer Klassiker ist die Mischung aus impliziten Konvertierungen und unterschiedlichen Präzisionsstufen. SQL Server hat dafür eigene Hinweise bei datetime und datetime2; mein praktischer Rat bleibt aber allgemeiner: Typen früh vereinheitlichen und Grenzen explizit setzen.
Wenn diese Basis stimmt, kannst du dieselbe Logik in fast jedem Projekt wiederverwenden, ohne ständig an Dialekt-Sonderfälle zu stoßen.
Wie ich solche Filter in echten Analysen einsetze
Im Alltag geht es selten um abstrakte Lehrbuchdaten, sondern um konkrete Entscheidungen. Drei typische Fälle zeigen, warum der Filter vor einem Datum so oft gebraucht wird:
-- 1. Bestellungen vor einem Kampagnenstart
SELECT order_id, order_date
FROM orders
WHERE order_date < '2026-06-01';Das ist die sauberste Form, wenn du einen Bericht nur für die Phase vor einer Aktion brauchst. Die Abgrenzung ist klar, und niemand muss später rätseln, ob der Starttag schon mitgezählt wurde.
-- 2. Logeinträge vor einem Wartungsfenster
SELECT *
FROM application_logs
WHERE created_at < '2026-06-10';Hier ist der Zeitanteil entscheidend. Der Vergleich schneidet alles vor dem Wartungsfenster ab und verhindert, dass Ereignisse aus demselben Tag fälschlich in die Voranalyse rutschen.
-- 3. Datensätze vor einem Stichtag für Compliance
SELECT customer_id, updated_at
FROM customer_audit
WHERE updated_at >= '2026-01-01'
AND updated_at < '2026-06-10';Solche Grenzen sind in Prüfungen oft wichtiger als jede weitere Aggregation. Wer hier präzise filtert, spart sich später Diskussionen über fehlende oder doppelt gezählte Datensätze.
Der praktische Gewinn ist immer derselbe: Die Abfrage erzählt exakt das, was sie tun soll, und nicht mehr.
Worauf ich vor dem produktiven Einsatz noch prüfe
Bevor ich so einen Filter in Reports, ETL-Jobs oder produktive Dashboards übernehme, gehe ich kurz fünf Punkte durch: Ist die Spalte wirklich vom passenden Typ? Ist die Grenze inklusiv oder exklusiv? Gibt es Zeitzonen im Spiel? Nutzt die Abfrage den vorhandenen Index? Und sind die Testdaten am Stichtag selbst geprüft worden?
Diese kurze Kontrolle spart in der Praxis mehr Zeit, als man denkt. Gerade bei historischen Analysen oder wiederkehrenden Monatsauswertungen ist die eigentliche Fehlerquelle selten SQL-Syntax, sondern eine unklare fachliche Grenze. Wenn du das Datum sauber definierst und die Vergleichslogik daran anpasst, wird aus einem einfachen Filter ein verlässliches Analysewerkzeug.
Für mich ist das die sinnvolle Faustregel: erst den fachlichen Stichtag präzise festlegen, dann den passenden Datentyp wählen und erst danach die Abfrage schreiben.