Wenn man mit SQL den letzten Tag des aktuellen Monats oder den ersten Tag der letzten Woche ausgeben möchte, steht man vor einer
Herausforderung. Die verschiedenen Datenbanksysteme wie MySQL, Microsoft SQL Server oder andere bietet dafür oft keine Standardfunkionen.
Hierzu ist es notwendig mehrere Funktionen zu kombinieren.
Die Herausforderung
Die gute Nachricht zuerst: Es ist problemlos möglich, verschiedene Datums-Tricks mit SQL
anzuwenden und der Weg ist in allen Datenbanksystemen sehr ähnlich. Lediglich die aufzurufenden Funktionen unterscheiden sich.
Mal angenommen, Sie möchten mit SQL einige Datumsangaben automatisiert ausgeben. Wenn man zum Beispiel den
letzten Tag des Monats Februar erhalten möchte, der sich aber ausgerechnet in einem Schaltjahr (wie 2016) befindet, steht man erst einmal
vor der Frage, wie man das korrekt ermittelt. Hierzu habe ich einige solcher häufig benötigter Datums-Angaben zusammengestellt und in SQL
formuliert.
Funktioniert immer - auch bei Schaltjahren
Diese Code-Schnipsel sind darauf ausgelegt, mit Hilfe der Datenbanksysteme immer das aktuelle Datum auszugeben.
Das gilt natürlich auch für Schaltjahre.
Kommentar im Code
Beschreibung
Bezugsdatum
Berechnetes Datum
-- current date
Aktuelles Datum
17.03.2016
17.03.2015
Woche
-- start of last week (begin: monday)
Beginn der letzten Woche
17.03.2016
07.03.2016
-- end of last week (begin: monday)
Ende der letzten Woche
17.03.2016
13.03.2016
-- start of current week (begin: monday)
Beginn der aktuellen Woche
17.03.2016
14.03.2016
-- end of current week (begin: monday)
Ende der aktuellen Woche
17.03.2016
20.03.2016
-- start of next week (begin: monday)
Beginn der nächsten Woche
17.03.2016
21.03.2016
-- end of next week (begin: monday)
Ende der nächsten Woche
17.03.2016
27.03.2016
Monat
-- start of last month
Beginn des letzten Monats
17.03.2016
01.02.2016
-- end of last month
Ende des letzten Monats
17.03.2016
29.02.2016
-- start of current month
Beginn des aktuellen Monats
17.03.2016
01.03.2016
-- end of current month
ende des aktuellen Monats
17.03.2016
31.03.2016
-- start of next month
Beginn des nächsten Monats
17.03.2016
01.04.2016
-- end of next month
Ende des nächsten Monats
17.03.2016
30.04.2016
Jahr
-- start of last year
Beginn des letzten Jahres
17.03.2016
01.01.2015
-- end of last year
Ende des letzten Jahres
17.03.2016
31.12.2015
-- start of current year
Beginn des aktuellen Jahres
17.03.2016
01.01.2016
-- end of current year
Ende des aktuellen Jahres
17.03.2016
31.12.2016
-- start of next year
Beginn des nächsten Jahres
17.03.2016
01.01.2017
-- end of next year
Ende des nächsten Jahres
17.03.2016
31.01.2017
Die Funktionsweise von Datums-Angaben mit SQL
Anhand einiger Beispiele möchte ich die Funktionsweise der unten stehenden Skripte erläutern.
Aktuelles Datum mit SQL
Zuerst wird immer das aktuelle Datum ermittelt. Dazu gibt es in den unterschiedlichen SQL-Dialekten
unterschiedliche Befehle. Bei einigen funktioniert CURRENT DATE (oder CURRENT_DATE mit
Unterstrich), bei anderen wird mit der Funktion NOW() oder es wird mittels CURRENT_TIMESTAMP gearbeitet.
Erster Tag des aktuellen Monats
Nachdem das Bezugsdatum, also das aktuelle Datum, ermittelt wurde, wird nach ständigen Konstanten gesucht. Beim ersten
Tag des Monats ist es immer Tag 1. Zusätzlich wird der Monat und das Jahr aus dem aktuellen Datum ermittelt. Diese Einzelteile
des Datums werden am Ende mit Hilfe einer passenden Funktion wieder zu einer Ausgabe mit dem Datums-Datentyp konvertiert.
Letzter Tag des aktuellen Monats
Bei der Ermittlung des letzten Tag des Monats wird es ein wenig komplizierter. Zuerst nimmt man an, es müsste geprüft werden,
um welchen Monat es sich handelt und dann entweder den Tag 30 oder 31 einsetzen. Doch was ist im Februar? Das ist einfach,
der Februar hat 28 Tage... aber Moment... was ist mit Schaltjahren? Dann hat der Februar 29 Tage.
Hier bedienen wir uns eines einfachen Tricks, der auch mit Programmiersprachen möglich ist.
Die SQL-Dialekte beinhalten meistens Datums-Funktionen, die das Rechnen ermöglichen. So können wir zum Beispiel
Tage addieren oder subtrahieren.
Am Beispiel des 17.02.2016 gilt also folgender Ablauf:
Aktuelles Datum ermitteln: 17.02.2016
Auf den ersten Tag des Monats kürzen: 01.02.2016
Einen Monat addieren: 01.03.2016
Einen Tag subtrahieren: 29.02.2016
Dieses Wissen kann man dann auch auf die anderen Szenarien, wie erster Tag des letzten Monats, letzter Tag
des nächsten Jahres usw. anwenden.
Ende der letzten Woche mit SQL
Bei den Wochen muss noch eine weitere Komponente berücksichtigt werden, damit der erste Tag der Woche und der letzte
Tag der Woche korrekt ermittelt werden. Mit Hilfe einer Funktion, die den numerischen Tag der Woche ausgibt,
kann dieses Problem gelöst werden.
Zum Beispiel ist Montag der 1. Tag und Sonntag der 7. Tag.
Hier unterscheiden sich die Datenbanksysteme am meisten voneinander. Einige geben als 1. Tag den Sonntag an,
manche arbeiten mit einem Index, beginnen also bei 0. Andere verwenden die Datums-Einstellungen des Computers, die den 1. Tag
auf Sonntag oder Montag festlegen. Hier muss im Zweifelsfall getestet werden, ob die berechneten Werte stimmen.
Es werden die Funktionen WEEKDAY(...) (MySQL), DATEPART(WEEKDAY, ...) (TSQL, Microsoft SQL Server) oder
DOW(...) (Sybase SQL Anywhere) verwendet.
Kompatibilität zwischen den SQL-Dialekten
Leider sind die Datums-Funktionen nicht im
SQL-92-Standard definiert,
der eine sehr große Kompatibilität sicherstellt.
Daher hat jeder Hersteller seine eigene Implementierung erstellt, die sich durch die Funktionsaufrufe der Datums-Berechnungen
unterscheiden und zusätzlich bei der Berechnung der Wochentage.
Datums-Berechnungen mit SQL
MySQL: DATE_ADD(CURRENT_DATE, INTERVAL -1 DAY)
TSQL: DATEADD(DAY, -1, CURRENT_TIMESTAMP)
Sybase SA: DATEADD(DAY, -1, CURRENT DATE)
Numerischen Wochentag mit SQL berechnen
MySQL: WEEKDAY(CURRENT_DATE)
TSQL: DATEPART(WEEKDAY, CURRENT_TIMESTAMP)
Sybase SA: DOW(CURRENT DATE)
Code für MySQL
-- current date
SELECT CURRENT_DATE AS date_current_date;
-- start of last week (begin: monday)
SELECT DATE_ADD(DATE_ADD(CURRENT_DATE, INTERVAL -WEEKDAY(CURRENT_DATE) DAY), INTERVAL -1 WEEK) AS start_of_last_week;
-- end of last week (begin: monday)
SELECT DATE_ADD(DATE_ADD(CURRENT_DATE, INTERVAL -WEEKDAY(CURRENT_DATE)+6 DAY), INTERVAL -1 WEEK) AS end_of_last_week;
-- start of current week (begin: monday)
SELECT DATE_ADD(CURRENT_DATE, INTERVAL -WEEKDAY(CURRENT_DATE) DAY) AS start_of_current_week;
-- end of current week (begin: monday)
SELECT DATE_ADD(CURRENT_DATE, INTERVAL -WEEKDAY(CURRENT_DATE)+6 DAY) AS end_of_current_week;
-- start of next week (begin: monday)
SELECT DATE_ADD(DATE_ADD(CURRENT_DATE, INTERVAL -WEEKDAY(CURRENT_DATE) DAY), INTERVAL 1 WEEK) AS start_of_next_week;
-- end of next week (begin: monday)
SELECT DATE_ADD(DATE_ADD(CURRENT_DATE, INTERVAL -WEEKDAY(CURRENT_DATE)+6 DAY), INTERVAL 1 WEEK) AS end_of_next_week;
-- start of last month
SELECT DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE), 1), INTERVAL MONTH(CURRENT_DATE)-2 MONTH) AS start_of_last_month;
-- end of last month
SELECT DATE_ADD(DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE), 1), INTERVAL MONTH(CURRENT_DATE)-1 MONTH), INTERVAL -1 DAY) AS end_of_last_month;
-- start of current month
SELECT DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE), 1), INTERVAL MONTH(CURRENT_DATE)-1 MONTH) AS start_of_current_month;
-- end of current month
SELECT DATE_ADD(DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE), 1), INTERVAL MONTH(CURRENT_DATE) MONTH), INTERVAL -1 DAY) AS end_of_current_month;
-- start of next month
SELECT DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE), 1), INTERVAL MONTH(CURRENT_DATE) MONTH) AS start_of_next_month;
-- end of next month
SELECT DATE_ADD(DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE), 1), INTERVAL MONTH(CURRENT_DATE)+1 MONTH), INTERVAL -1 DAY) AS end_of_next_month;
-- start of last year
SELECT MAKEDATE(YEAR(CURRENT_DATE)-1, 1) AS start_of_last_year;
-- end of last year
SELECT DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE), 1), INTERVAL -1 DAY) AS end_of_last_year;
-- start of current year
SELECT MAKEDATE(YEAR(CURRENT_DATE), 1) AS start_of_current_year;
-- end of current year
SELECT DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE)+1, 1), INTERVAL -1 DAY) AS end_of_current_year;
-- start of next year
SELECT MAKEDATE(YEAR(CURRENT_DATE)+1, 1) AS start_of_next_year;
-- end of next year
SELECT DATE_ADD(MAKEDATE(YEAR(CURRENT_DATE)+2, 1), INTERVAL -1 DAY) AS end_of_next_year;
Code für SQL Server (TSQL)
-- current date
SELECT CAST(CURRENT_TIMESTAMP AS DATE) AS date_current_date
-- start of last week (begin: monday)
SELECT CAST(DATEADD(WEEK, -1 ,DATEADD(DAY, -DATEPART(WEEKDAY, CURRENT_TIMESTAMP) + 1, CURRENT_TIMESTAMP)) AS DATE) AS start_of_last_week
-- end of last week (begin: monday)
SELECT CAST(DATEADD(WEEK, -1, DATEADD(DAY, -DATEPART(WEEKDAY, CURRENT_TIMESTAMP) + 7, CURRENT_TIMESTAMP)) AS DATE) AS end_of_last_week
-- start of current week (begin: monday)
SELECT CAST(DATEADD(DAY, -DATEPART(WEEKDAY, CURRENT_TIMESTAMP) + 1, CURRENT_TIMESTAMP) AS DATE) AS start_of_current_week
-- end of current week (begins monday)
SELECT CAST(DATEADD(DAY, -DATEPART(WEEKDAY, CURRENT_TIMESTAMP) + 7, CURRENT_TIMESTAMP) AS DATE) AS end_of_current_week
-- start of next week (begin: monday)
SELECT CAST(DATEADD(WEEK, 1 ,DATEADD(DAY, -DATEPART(WEEKDAY, CURRENT_TIMESTAMP) + 1, CURRENT_TIMESTAMP)) AS DATE) AS start_of_next_week
-- end of next week (begin: monday)
SELECT CAST(DATEADD(WEEK, 1, DATEADD(DAY, -DATEPART(WEEKDAY, CURRENT_TIMESTAMP) + 7, CURRENT_TIMESTAMP)) AS DATE) AS end_of_next_week
-- start of last month
SELECT CAST(DATEADD(MONTH, -1 , DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP),MONTH(CURRENT_TIMESTAMP),1)) AS DATE) AS start_of_last_month
-- end of last month
SELECT CAST(DATEADD(DAY, -1 , DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP),MONTH(CURRENT_TIMESTAMP),1)) AS DATE) AS end_of_last_month
-- start of current month
SELECT DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP),MONTH(CURRENT_TIMESTAMP),1) AS start_of_current_month
-- end of current month
SELECT DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP),MONTH(CURRENT_TIMESTAMP),1))) AS end_of_current_month
-- start of next month
SELECT DATEADD(MONTH, 1, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP),MONTH(CURRENT_TIMESTAMP), 1)) AS start_of_next_month
-- end of next month
SELECT DATEADD(DAY, -1, DATEADD(MONTH, 2, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP),MONTH(CURRENT_TIMESTAMP), 1))) AS end_of_next_month
-- start of last year
SELECT DATEADD(YEAR, -1, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP), 1, 1)) AS start_of_last_year
-- end of last year
SELECT DATEADD(DAY, -1, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP), 1, 1)) AS end_of_last_year
-- start of current year
SELECT DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP), 1, 1) AS start_of_current_year
-- end of current year
SELECT DATEADD(DAY, -1, DATEADD(YEAR, 1, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP), 1, 1))) AS start_of_current_year
-- start of next year
SELECT DATEADD(YEAR, 1, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP), 1, 1)) AS start_of_next_year
-- end of next year
SELECT DATEADD(DAY, -1, DATEADD(YEAR, 2, DATEFROMPARTS(YEAR(CURRENT_TIMESTAMP), 1, 1))) AS end_of_next_year
Code für Sybase SQL Anywhere
-- current date
SELECT CURRENT DATE AS date_current_date
-- start of last week (begin: monday)
SELECT CAST(DATEADD(WEEK, -1, DATEADD(DAY, -DOW(CURRENT DATE)+2, CURRENT DATE)) AS DATE) AS start_of_last_week
-- end of last week (begin: monday)
SELECT CAST(DATEADD(WEEK, -1, DATEADD(DAY, -DOW(CURRENT DATE)+8, CURRENT DATE)) AS DATE) AS end_of_last_week
-- start of current week (begin: monday)
SELECT CAST(DATEADD(DAY, -DOW(CURRENT DATE)+2, CURRENT DATE) AS DATE) AS start_of_current_week
-- end of current week (begin: monday)
SELECT CAST(DATEADD(DAY, -DOW(CURRENT DATE)+8, CURRENT DATE) AS DATE) AS end_of_current_week
-- start of next week (begin: monday)
SELECT CAST(DATEADD(WEEK, 1, DATEADD(DAY, -DOW(CURRENT DATE)+2, CURRENT DATE)) AS DATE) AS start_of_next_week
-- end of next week (begin: monday)
SELECT CAST(DATEADD(WEEK, 1, DATEADD(DAY, -DOW(CURRENT DATE)+8, CURRENT DATE)) AS DATE) AS end_of_next_week
-- start of last month
SELECT CAST(DATEADD(MONTH, -1, YMD(YEAR(CURRENT DATE), MONTH(CURRENT DATE), 1)) AS DATE) AS start_as_last_month
-- end of last month
SELECT CAST(DATEADD(DAY, -1, YMD(YEAR(CURRENT DATE), MONTH(CURRENT DATE), 1)) AS DATE) AS end_of_last_month
-- start of current month
SELECT YMD(YEAR(CURRENT DATE), MONTH(CURRENT DATE), 1) AS start_of_current_month
-- end of current month
SELECT CAST(DATEADD(MONTH, 1, YMD(YEAR(CURRENT DATE), MONTH(CURRENT DATE), 1)) AS DATE) AS end_of_current_month
-- start of next month
SELECT CAST(DATEADD(MONTH, 1, YMD(YEAR(CURRENT DATE), MONTH(CURRENT DATE), 1)) AS DATE) AS start_of_next_month
-- end of next month
SELECT CAST(DATEADD(DAY, -1, DATEADD(MONTH, 2 ,YMD(YEAR(CURRENT DATE), MONTH(CURRENT DATE), 1))) AS DATE) AS end_of_next_month
-- start of last year
SELECT CAST(DATEADD(YEAR, -1, YMD(YEAR(CURRENT DATE), 1, 1)) AS DATE) AS start_of_last_year
-- end of last year
SELECT CAST(DATEADD(DAY, -1, YMD(YEAR(CURRENT DATE), 1, 1)) AS DATE) AS end_of_last_year
-- start of current year
SELECT YMD(YEAR(CURRENT DATE), 1, 1) AS start_of_current_year
-- end of current year
SELECT CAST(DATEADD(DAY, -1, DATEADD(YEAR, 1, YMD(YEAR(CURRENT DATE), 1, 1))) AS DATE) AS end_of_current_year
-- start of next year
SELECT CAST(DATEADD(YEAR, 1, YMD(YEAR(CURRENT DATE), 1, 1)) AS DATE) AS start_of_next_year
-- end of next year
SELECT CAST(DATEADD(DAY, -1, DATEADD(YEAR, 2, YMD(YEAR(CURRENT DATE), 1, 1))) AS DATE) AS end_of_next_year