Startseite
Impressum
Skills
Referenzen
pfeil Produkte
Links
pfeil Tips
Benutzerverwaltung 1
Benutzerverwaltung 2
Das ideale Paßwort
Datum und SQL-DB
DSL-Probleme
Dr. Watson Teil 1
Dr. Watson Teil 2
DTS Automatisierung
Installation autom.
Kalender und Zeit
Mail Automatisierung
Office Automation
Stack Frame
SQL Server
Strings + Unicode
VB 6 Tips & Tricks
Windows-Uhrzeit

 

Drucker - freundliche Darstellung


  zalando.de - Schuhe und Fashion online

 

Letzte Aktualisierung: 14.11.2015

handyservice.de


Datumswerte in Datenbanken

Jeder Entwickler wird früher oder später Datums- und Uhrzeitwerte in einer Datenbank verwalten müssen, und jeder wird früher oder später auf Probleme damit stoßen. Denn das Format eines Datums wird international recht unterschiedlich gehandhabt, und dies führt spätestens dann zu Problemen, wenn man eine Applikation in mehreren Ländern oder Sprachen betreibt, oder auch schon bei verschiedenen Versionen der zugrundeliegenden Datenbank.

Ich beschreibe im Folgenden speziell Probleme und Lösungen mit Microsoft SQL Server, die Anwendung gelingt analog aber auch mit anderen Datenbanken.

Ein Plädoyer für korrekte Datentypen

Welche Probleme entstehen, und wie werden Sie verhindert ?

Buchtips

Ein Plädoyer für korrekte Datentypen

Wann immer ein Datumswert in einer Datenbank verwendet werden soll, stellt sich schon bei der Planung die Frage, welcher Datentyp verwendet werden soll. Eine vermeintlich einfache Lösung ohne große Probleme bietet ein Textfeld (also char oder varchar), in welches man die Datumswerte einfügt in der Form 'DD.MM.YYYY' (Ich werde im Folgenden immer die Buchstaben DD für die zweistellige Darstellung des Tages, MM für den Monat und YYYY für das Jahr verwenden). Vermeintlich deshalb, weil diese Lösung einige Vorteile bietet, die aber durch gravierende Nachteile erkauft werden:

    1. Man braucht sich keine Gedanken um das internationale Format zu machen, denn die Darstellung ist statisch festgelegt. Es gibt zunächst keine Konvertierungsprobleme, weil man ja am Anfang stets nur ein einer Sprache, in einem festen Format arbeitet. ABER: Wehe, wenn das Programm superklasse ist, und der erste ausländische Kunde bestellt!

    2. Ich muß mir keine Gedanken machen über die Validierung meiner Datumswerte, denn auch ein 30.02.2005 wird von der Datenbank akzeptiert ohne lästige Fehlermeldungen. ABER: Auch der Wert 'oxporgl' wird akzeptiert, und hier wird spätestens jedem klar, welche Gefahren damit lauern. In datetime / smalldatetime - Felder kann man solche Werte gar nicht erst füllen.

    3. Ich kann nach einem exakten Datum Suchen, also zum Beispiel mit einem SQL - Befehl der Art "SELECT ... WHERE Datum = '15.05.2005'", und brauche keine BETWEEN - Technik zu betreiben (wird gleich beschrieben). ABER: Dies kostet ganz wesentlich Performance (siehe unten).

Nachteile, die nicht sofort ersichtlich sind:

    4. Performance I. Für die oben gewählte Darstellung benötigt man im Falle von char(10) stets 10 Bytes, im Falle von varchar(10) sogar noch mehr, plus der potentiellen Auslagerung der Daten auf andere Datenbank-Pages. Der Datentyp datetime benötigt bloß 8 Bytes, smalldatetime sogar nur 4 Bytes pro Datensatz. Klar, daß schon die schiere Masse an Daten auf die Performance drückt.

    5. Performance II. Bei jeder Suche, die irgendeine Vergleichsoperation für einen Datumswert umfaßt, muß ein Stringvergleich über alle (mindestens) 10 Bytes vollzogen werden, bei dem Byte für Byte im Maschinencode verglichen werden muß. Datetime und smalldatetime sind numerische Werte, hier wird ein Vergleich mit einer einzigen Maschineninstruktion durchgeführt. Dazu kommt beim Datentyp varchar(10) noch potentielles Seitenflattern, weil die Datumswerte potentiell auf einer anderen Page liegen als die der eigentlichen Daten - Page; ein gigantischer Performancenachteil im Bereich mehrerer Zehnerpotenzen, wenn Festplattenoperationen nötig sind. Aber auch im best case, also ohne page faults, dürften die Unterschiede stets mindestens den Faktor 10 ausmachen, man müßte im Einzelfall einmal nachmessen.

    6. Genauigkeit. Bei datetime und smalldatetime bekomme ich sogar noch die Uhrzeit kostenlos mit gespeichert, im selben Wert, mindestens auf die Minute genau. Dies wird aber häufig nicht benötigt und muß kein Nachteil sein.

    7. Vergleichsoperationen. Suchen Sie mal mit Zeichenketten nach allen Datumswerten, die im Bereich der letzten 12 Monate liegen. Merken Sie etwas ?
    Ist der String - Wert '02.03.2004' größer oder kleiner als '01.03.2005' ? Probieren Sie es mal aus :-) Bei (small)datetime - Feldern ist es absolut eindeutig, wenn Sie angeben "WHERE Auftragsdatum >= '15.05.2005'"

Man sieht also, daß es durchaus Sinn macht, (small)datetime - Felder zu verwenden, denn dafür sind sie auch geschaffen worden.
Datetime ist bis auf die 300stel Sekunde genau, smalldatetime immerhin noch bis auf die Minute genau.

Welche Probleme entstehen, und wie werden Sie verhindert ?

Auf Datenbanksystemen, die in unterschiedlichen Sprachen installiert worden sind, werden Datumswerte technisch zwar identisch gespeichert, aber unterschiedlich ausgegeben. Machen Sie ein SELECT auf einem deutschen System, bekommen Sie das Format 'DD.MM.YYYY HH:MM:SS'. Auf einem englischen System bekommen Sie standardmäßig 'YYYY-MM-DD HH:MM:SS'. Man kann dieses Problem durch verschiedene Maßnahmen umgehen. Wenn man eine Datenbank-Connection geöffnet hat, reicht es z.B. aus, aufzurufen "SET DATEFORMAT mdy", und die Ergebnisse nach dem Select lassen sich alle mit deutscher Formatierung weiterverarbeiten. Viel Konsequenter und sicherer ist es jedoch, die "mehrsprachigkeit" von Datenbankservern von vornherein einzuplanen, und bei der Programmierung von Datenbankabfragen die entsprechende Konvertierung vorzusehen, denn dann erspart man sich die Überraschungen, wenn man den genannten Aufruf einmal vergißt. Ein Beispiel:

SELECT CONVERT(char(10), MyDatetime, 104) FROM Tabelle

wird mir immer das Format 'DD.MM.YYYY' zurückgeben, egal, mit welcher Sprache der Datenbankserver betrieben wird. MyDatetime ist dabei ein smalldatetime oder datetime - Wert.

Ein weiteres Beispiel:

UPDATE Tabelle SET MyDatetime = CONVERT(datetime, stringvariable, 104)

wird mir ebenfalls bei einer stringvariable im genannten deutschen Format immer ein korrektes Datum in den Server schreiben.

Warum 104 ? Es gibt verschiedene vordefinierte Formate, die numeriert sind, eine vollständige Übersicht findet man unter CAST and CONVERT.

Dort kann man sich auch für andere Sprachen oder Formate etwas geeignetes auswählen.

Auch innerhalb von stored procedures, views und überhaupt überall, wo man SQL - Code benutzt, sollte man so verfahren, wie hier gezeigt. Eine Einschränkung bilden Parameterlisten von stored procedures und views: Aufrufe der folgenden Art sind nicht möglich, wenn Datumswerte als Parameter verlangt werden:

sp_ZeigeAlleGrufties @GebDat=CONVERT(datetime, stringvariable, 104)

Leider akzeptiert der SQL Server dies nicht, daher kann ich nur dringend raten, in Parameterlisten keine datetime oder smalldatetime - Werte zu verwenden, sondern Strings in der folgenden Form:

CREATE PROC dbo.sp_ZeigeAlleGrufties @GebDatImFormat104 varchar(10) AS DECLARE @GebDatum datetime SELECT @GebDatum = CONVERT(datetime, @GebDatImFormat104, 104) ...

Dadurch halte ich es mir stets offen, auf welchem Server meine stored procedures nun Laufen, und sorge bei der Übergabe an die stored procedure dafür, daß der String das Format 104 (oder welches auch immer) hat.

Nun zur BETWEEN - Technik, die ich oben schon genannt habe. Es kann ziemlich tückisch werden, wenn man mit datetimes und smalldatetimes arbeitet, und dabei SELECT - Abfragen der Form "WHERE DATUM = '15.05.2005'" aufruft; denn dabei kommen nur die Werte zurück, die als Uhrzeit "00:00:00" haben, und dies ist häufig nicht der Fall, manchmal ändert sich dies sogar schleichend im Lauf des Lebens einer Anwendung. Sie erinnern sich, die Uhrzeit ist stets fester Bestandteil eines (small)datetime - Wertes. Die meisten Datenbankeinträge erfolgen erfahrungsgemäß in der Form

INSERT INTO Auftrag (..., Auftragsdatum, ...) VALUES (..., getdate(), ...)

indem also der aktuelle Datumswert automatisiert eingefügt wird, und dabei kommt die Uhrzeit eben gleich mit, was im Übrigen für statistische Auswertung oft gar nicht so schlecht ist.

Daher können die Aufträge des 15.05.2005 nur auf diesem Weg selektiert werden:

SELECT * FROM Auftrag WHERE Auftragsdatum BETWEEN CONVERT(datetime, '15.05.2005 00:00:00', 104) AND CONVERT(datetime, '15.05.2005 23:59:59', 104)

Buchtips

"Microsoft SQL Server 2008 Internals" von Kalen Delaney, das offizielle Nachfolgebuch des Klassikers Soukup (ich besitze beide Bücher seit vielen Jahren). Ein must-have für den Datenbank - Administrator. Hier geht es weniger um die Programmierung (naja, "nur" um etwas T-SQL-Programmierung, wenn man so will), sondern um das Innenleben des SQL - Servers, um Administrationsfragen, Performance - Tuning und ein solides Grundverständnis. In englisch.

Zwei Bücher, mit denen die Programmierung von Scripten oder Programmen auf Basis des SQL Servers umfassend erlernt werden kann. Es wird gezeigt, wie man mit Transact-SQL auf Daten zugreift und Daten manipuliert, wie man man mächtige Windows-Applikationen mit ADO, DAO, ODBC und RDO "baut" und wie man mit ASP und XML Web-basierte Datenbank-Anwendungen entwickelt. Neben Highend-Entwickler-Know-how, das, wie der Titel schon nahelegt, den Schwerpunkt dieses Buches ausmacht, vermittelt dieses Buch auch wichtiges Administratoren-Wissen, egal, ob es um die Performance, die Verwaltung, die Sicherung (Backup & Recovery) oder die Sicherheit (Security) der Datenbank geht.

Inside Microsoft SQL Server 2005: The Storage Engine von Kalen Delaney befaßt sich weniger mit einer allgemeinen Beschreibung des SQL Servers, sondern mit dem Datenbank-Kern, der sogenannten Storage Engine. Ein Buch für Profis, die das letzte Fitzelchen Performance aus dem SQL Server rausholen wollen.

Support, Feedback, Anregungen

Alles verstanden ? Oder noch Fragen ? Wir freuen uns über Feedback zu den Tips, auch über Verbesserungsvorschläge. Schreiben Sie an support@a-m-i.de.