Zeitstempel in Datenbanken (Oracle)
Zu meinem Job als Software Architekt gehört es an Guidelines für die Implementierung von Software Systemen mitzuarbeiten. Ganz konkret habe ich mich etwas in das Thema Datum/Zeit Speicherung in Datenbanksystemen gestürzt.
Aus dem Grund kann ich hier etwas über die Datum/Zeit Repräsentation in Oracle DB 10g Release 1 (und natürlich älteren Modellen) berichten. Gerade im Zusammenspiel mit Zugriffen via JDBC sind hier einige Dinge zu beachten.
Beim Design unserer Software achten wir auf die TCO. Das klingt zwar wie ein Buzz-Word, macht aber durchaus Sinn: das System soll möglichst wenig Anforderungen an die Konfiguration der Datenbank stellen (damit diese für unterschiedliche Anwendungen genutzt werden kann). Ebenso soll es möglichst unempfindlich gegen Konfigurationsänderungen und Variationen sein (gerade im Bereich Zeit/Datum können Probleme mit unterschiedlichen Datums-Formaten oder Sprachen auftreten.)
Oracle Datum/Zeit Datentypen
Prinzipiell unterstützt Oracle den SQL Datentyp "DATE", erweitert um Zeitinformationen, bis hin zur Sekunde. Der Typ hat keine Möglichkeit der Speicherung von Schaltsekunden. Er belegt 7 bytes, wobei Jahrhundert, Jahr, Monat, Tag, Stunde, Minute und Sekunde in getrennten Feldern gespeichert werden. Es werden keine zusätzlichen Zeitzonen-Informationen gespeichert und der Wertebereich reicht von v.Chr 4712-12-31 00:00 bis 9999-12-31 23:59:59.
Das Speicherformat eines Datenbank Feldes oder eines temporären Typen kann man mit Oracle leicht mit der dump() Funktion inspizieren:
SQL> INSERT INTO schema.tabelle (datumspalte) VALUES (TO_DATE('18-FEB-2005 23:12:19', 'dd-Mon-yyyy HH24:mi:SS');
SQL> SELECT datumspalte,dump(datumsspalte) FROM schema.tabelle;
2005-02-18 23:12:19
Typ=12 Len=7: 120,105,02,18,24,13,20
Jahrhundert und Jahr werden mit einem 100 Offset, Stunde, Minute und Sekunde mit einem Offset von 1 gespeichert, um 0-bytes zu vermeiden. (Und um Jahreszahlen vor Christus darzustellen. Das Jahr 0 gibt es bei Oracle nicht).
Zwischenergebnisse und Systemfunktionen kennen des weiteren noch einen internen DATE Typ, der aber Plattform-spezifisch ist:
SQL> SELECT dump(SYSDATE) FROM dual;
Typ=13 Len=8: 213,7,2,18,23,12,19,0 // 7*256+213=2005
Hier wird die Zeit 2005-02-18 23:12:19 auf x86 dargestellt.
Es ist natürlich unbefriedigend, dass Oracle hier keine genauere Zeitauflösung bietet, und auch die Unterstützung von UTC Zeiten bzw. Zeitzonen fehlen. Deswegen bietet Oracle zwei weitere Datentypen für die Darstellung von Zeitstempeln an. Diese bieten eine Auflösung von Sekunden mit 0-9 Nachkommastellen. Es handelt sich dabei im den Typ TIMESTAMP oder TIMESTAMP WITH TIME ZONE. Wie auch schon bei DATE gibt es hier eine interne Repräsentation, damit umfasst die Liste der Datum/Zeit Typen (ohne Intervalle):
Die Anzahl der Sekunden Nachkommastellen hat, wie man in der Tabelle sehen kann keine Auswirkung auf das Speicherformat (außer bei Typen ohne Sekundenbruchteile). Wenn keine Precision bei TIMESTAMP angegeben wurde sind dies 6 Stellen, das entspricht der Darstellung als Java Millisekunden. Mit dem Maximum von 9 Stellen können Nanosekunden wie in java.sql.TIMESTAMP#getNanos() abgedeckt werden.
Typ | Typ Name | Bytes |
---|---|---|
12 | DATE | 7 |
180 | TIMESTAMP(0) | 7 |
180 | TIMESTAMP(1-9) | 11 |
181 | TIMESTAMP(0) WITH ZIME ZONE | 13 |
181 | TIMESTAMP(1-9) WITH ZIME ZONE | 13 |
231 | TIMESTAMP(0) WITH LOCAL TIME ZONE | 7 |
231 | TIMESTAMP(1-9) WITH LOCAL TIME ZONE | 11 |
13 | intern DATE | 8 |
188 | intern TIMESTAMP | 20 |
Literale
Wenn in einem Ausdruck ein Zeichenstring als DATUM oder TIMESTAMP interpretiert werden soll, so führt Oracle eine Implizite Konvertierung durch. Diese implizite Konvertierung ist aber abhängig von den NLS Einstellungen, und sollte deswegen nie verwendet werden. Besser ist es Zeitstempel explizit mit Funktionen zu konvertieren, oder als Literal anzugeben. Beim DATE Typ wird allerdings nur das ANSI SQL Literal unterstützt: DATE '2005-02-18' also keine Zeitwerte. Hier ist es unvermeidlich die Konvertierungsfunktion zu verwenden: TO_DATE('2005-02-18 23:12:19', 'YYYY-MM-DD HH24:MI:SS') Das TIMESTAMP Literal ist hier deutlich praktischer: TIMESTAMP '2005-02-18 23:12:19.123456'. Und gibt es auch in einer Version mit Zeitzonen oder Zeitoffsets: TIMESTAMP '2005-02-18 23:12:19.123 +01:00' TIMESTAMP '2005-02-18 23:12:19.123 Europe/Berlin CET' (Die Angabe von Zeitzonen Namen erfordert zusätzlich die Angabe des kurzen Zeitzonen Namens zur Unterscheidung von Sommer/Winterzeit. Es ist deswegen in den meisten Situationen deutlich besser nur mit Zeit-Offsets zu arbeiten)Zeitzonen
Informationen zur Zeitzone können als Offset (In Stunden und Minuten) von der UTC Zeit dargestellt werden. Jedoch sind zusätzlich auch Informationen zur Start/Ende Zeit der Sommerzeit notwendig. Bei einer verteilten Anwendung können hier verschiedene Zeitzonen zusammenspielen: Zeitzone des DB-Server Betriebsystems, Zeitzone des DB-Client Betriebsystems. Zeitzone der Datenbank, Zeitzone der Client Sitzung (Session). Bei einer 3tier Anwendung kommt dann noch die Zeitzone dazu die Benutzer am Web/Client Front-end sehen möchten. Die Zeitzone der Datenbank wird bei der Einrichtung aus der Zeitzone des DB-Server Betriebsystems initialisiert, kann aber mit "ALTER DATABASE SET time_zone = 'UTC';" gesetzt werden (Restart erforderlich). Diese Zeitzone wird nur als Basis für den Typ "TIMESTAMP WITH LOCAL TIME ZONE" benutzt. Oracle empfiehlt die Verwendung von UTC Zeiten um Datensynchronisation und Backup/Restore zwischen verschiedenen Standorten zu erleichtern. Die Zeitzone der Client Session wird aus der Zeitzone des Client Betriebsystems ermittelt, lässt sich über eine Environment Variable vorgeben, oder mittels "ALTER SESSION SET time_zone = 'Europe/Berlin';" setzen. Die Session Zeitzone wird benutzt bei der Speicherung von Daten in die Felder "TIMESTAMP WITH [local] TIME ZONE", es sei denn eine Zeitzone wurde explizit angegeben. Die Betriebsystem Zeitzone des DB-Servers wird immer für die Ausgabe der Systemfunktionen SYSDATE und SYSTIMESTAMP verwendet. Da die Zeitstempel in einem "TIMESTAMP WITH LOCAL TIME ZONE" Feld immer auf die Zeitzone der Datenbank normiert werden, erlaubt Oracle die Veränderung der Datenbank Zeitzone nicht, wenn diese Daten vorliegen. Ebenfalls sind die Daten bei einem Backup dann immer relativ zu dieser Zeitzone. Im Sinne der Reduktion von Abhängigkeiten ist es meiner Meinung nach geboten, auf diesen Datentyp gänzlich zu verzichten.Oracle und JDBC
So weit sind die Fakten klar, und man kann z.B. entscheiden alle Zeiten immer als UTC in einen TIMESTAMP Typen zu speichern, dabei die Zeitzone der DB und des Servers ignorieren, keine NLS Formatstrings implizit zu verwenden. Die Auswahl von UTC als Speicherzeitzone hat einige Vorteile:- es sind keine zusätzlichen bytes zur Speicherung der Zeitzone notwendig
- die DB Zeitzone kann jederzeit geändert werden, oder das Backup kann jederzeit in eine DB mit anderer Zeitzone eingespielt werden, da diese einfach nicht beachtet wird
- Interpretationen bezüglich der gewünschten Zeitzone/DST fallen nicht an, somit sind DB und Session Zeitzone unerheblich
Zum weiterlesen
- Oracle® Database Globalization Support Guide
10g Release 1 (10.1) Part Number B10749-024 Datetime Datatypes and Time Zone Support - DBASupport.com: Oracle Time Zone
- OraBlogs
Comments
Display comments as Linear | Threaded
Thomas on :
eckes on :
Thomas on :
eckes on :
Thomas on :
eckes on :
Thomas on :
eckes on :
Thomas on :
eckes on :
Rudolf Galfi on :
Kai-Uwe Schaefer on :
Bernd Eckenfels on :
Fr34k on :
Bernd Eckenfels on :