Gültigkeitszeit und Erfassungszeit sauber getrennt halten
Die meisten Datenmodelle kennen nur eine Zeitachse: wann eine Zeile zuletzt geändert wurde. Sobald aber rückwirkende Korrekturen ins Spiel kommen, etwa eine falsch berechnete Rechnung, die drei Monate später korrigiert wird, reicht eine einzige Zeitachse nicht mehr aus. Bitemporale Modellierung unterscheidet konsequent zwischen der Gültigkeitszeit, also wann ein Sachverhalt fachlich galt, und der Erfassungszeit, also wann er im System bekannt wurde. Dieser Artikel erklärt beide Achsen, zeigt ein durchgerechnetes Beispiel und ordnet ein, wann sich der zusätzliche Modellierungsaufwand tatsächlich lohnt.
Inhaltsverzeichnis
- 1. Warum ein Datensatz oft zwei unabhängige Zeitachsen braucht
- 2. Die Gültigkeitszeit: wann ein Sachverhalt fachlich galt
- 3. Die Erfassungszeit: wann eine Information im System bekannt wurde
- 4. Beide Achsen kombiniert: eine bitemporale Tabelle mit vier Zeitstempeln
- 5. Praxisbeispiel: eine rückwirkende Preiskorrektur nachvollziehbar halten
- 6. Abfragen mit kombinierten Zeitpunkten: as of valid time und as of transaction time
- 7. Modellierungsmuster: getrennte Achsen kombinieren statt neu erfinden
- 8. Den Modellierungsaufwand realistisch einordnen
- 9. Typische Fehler bei der Einführung bitemporaler Modelle
- 10. Zusammenfassung
- 11. FAQ
1. Warum ein Datensatz oft zwei unabhängige Zeitachsen braucht
Eine einzelne Zeitspalte wie updated_at beantwortet nur eine Frage: Wann wurde diese Zeile zuletzt physisch geschrieben. Sie beantwortet nicht die viel wichtigere fachliche Frage, ab wann ein Wert eigentlich gelten sollte. Bei einer nachträglichen Korrektur, etwa weil ein Preis am 1. Januar falsch im System hinterlegt und erst am 15. März bemerkt wurde, verschmelzen diese beiden Fragen in einer einzigen Zeitspalte zu einer einzigen, unbrauchbaren Antwort.
Bitemporale Modellierung trennt diese beiden Fragen konsequent in zwei unabhängige Intervalle: die Gültigkeitszeit, im Englischen Valid Time genannt, beschreibt den fachlichen Zeitraum, für den ein Wert gilt, während die Erfassungszeit, im Englischen Transaction Time, beschreibt, wann diese Information im System als bekannt galt. Beide Achsen zusammen erlauben Fragen wie: Was wussten wir am 1. Februar über den Preis, der am 1. Januar galt.
2. Die Gültigkeitszeit: wann ein Sachverhalt fachlich galt
Die Gültigkeitszeit wird üblicherweise von der Fachlogik oder von einer Anwenderin gesetzt, nicht automatisch von der Datenbank. Sie beschreibt einen Zeitraum aus der realen Welt: Ein Mitarbeitergehalt gilt ab dem 1. eines Monats, eine Preisänderung gilt ab dem Tag einer Vertragsunterzeichnung, ein Rabatt gilt für einen festgelegten Aktionszeitraum. Dieser Zeitraum kann auch in der Zukunft liegen, etwa wenn eine Gehaltserhöhung heute erfasst, aber erst zum nächsten Monatsersten wirksam wird.
Wichtig ist: Die Gültigkeitszeit kann nachträglich korrigiert werden, ohne dass sich dabei etwas an der Erfassungszeit ändert. Wird ein Fehler in der ursprünglichen Gültigkeitsangabe entdeckt, entsteht eine neue Zeile mit korrigierter Gültigkeitszeit, aber eigener, neuer Erfassungszeit, während die fehlerhafte alte Zeile in der Historie nachvollziehbar erhalten bleibt.
3. Die Erfassungszeit: wann eine Information im System bekannt wurde
Die Erfassungszeit wird dagegen fast immer automatisch von der Datenbank oder der Anwendung gesetzt und ist im Nachhinein grundsätzlich unveränderlich, denn sie beschreibt einen Fakt über das System selbst: Zu welchem Zeitpunkt wusste diese Datenbank von diesem Wert. Genau diese Eigenschaft macht die Erfassungszeit zur verlässlichen Grundlage für Audits, denn sie kann per Definition nicht rückwirkend manipuliert werden, ohne dass das an der Datenstruktur sichtbar würde.
In der Praxis besteht die Erfassungszeit meist aus zwei Zeitstempeln, analog zum System-Versioning: einem Startzeitpunkt, ab dem diese Version einer Zeile als aktuell bekannter Stand galt, und einem Endzeitpunkt, ab dem sie durch eine neuere Version ersetzt wurde. Solange eine Zeile der aktuell bekannte Stand ist, bleibt ihr Erfassungs-Ende typischerweise auf einen Maximalwert wie 9999-12-31 gesetzt.
4. Beide Achsen kombiniert: eine bitemporale Tabelle mit vier Zeitstempeln
In der Tabelle selbst führt das zu vier Zeitstempel-Spalten statt einer: gueltig_von und gueltig_bis für die fachliche Gültigkeitszeit, sowie erfasst_von und erfasst_bis für die technische Erfassungszeit. Jede Zeile beschreibt damit exakt eine Kombination aus einer Gültigkeitsperiode und einer Erfassungsperiode, und eine Korrektur erzeugt niemals ein UPDATE auf einer bestehenden Zeile, sondern immer eine neue Zeile mit neuer Erfassungszeit.
Diese Struktur wirkt auf den ersten Blick ungewohnt, weil dieselbe fachliche Gültigkeitsperiode über mehrere Zeilen mit unterschiedlichen Erfassungszeiten verteilt sein kann, sobald eine rückwirkende Korrektur vorgenommen wurde. Genau das ist aber der Punkt: Die Tabelle hält nicht nur den aktuell korrekten Wert vor, sondern auch die komplette Historie dessen, was zu welchem Zeitpunkt als richtig galt.
CREATE TABLE preis_bitemporal (
preis_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
produkt_id INT NOT NULL,
preis_cent INT NOT NULL,
gueltig_von DATE NOT NULL,
gueltig_bis DATE NOT NULL DEFAULT '9999-12-31',
erfasst_von TIMESTAMP NOT NULL DEFAULT now(),
erfasst_bis TIMESTAMP NOT NULL DEFAULT '9999-12-31 00:00:00'
);
5. Praxisbeispiel: eine rückwirkende Preiskorrektur nachvollziehbar halten
Am 1. Januar wird ein Produktpreis von 4000 Cent erfasst, gültig ab demselben Tag. Am 15. März stellt sich heraus, dass der Preis tatsächlich schon ab dem 1. Januar 4500 Cent hätte betragen müssen, ein reiner Erfassungsfehler bei der ursprünglichen Eingabe. Statt die alte Zeile einfach zu überschreiben, wird sie mit erfasst_bis auf den 15. März geschlossen, und eine neue Zeile mit gueltig_von zum 1. Januar, aber erfasst_von zum 15. März wird eingefügt.
Damit lässt sich später exakt beantworten, was ein Bericht vom 1. Februar gezeigt hätte, nämlich noch 4000 Cent, obwohl der fachlich korrekte Preis für diesen Tag inzwischen bekanntermaßen 4500 Cent war. Genau diese Unterscheidung ist für Finanzberichte, die zu einem bestimmten Stichtag reproduzierbar sein müssen, oft unverzichtbar.
-- Alte Zeile als überholt markieren (Erfassungszeit schließen)
UPDATE preis_bitemporal
SET erfasst_bis = '2026-03-15 09:00:00'
WHERE produkt_id = 100 AND erfasst_bis = '9999-12-31 00:00:00';
-- Korrigierten Wert mit rückwirkender Gültigkeit neu erfassen
INSERT INTO preis_bitemporal
(produkt_id, preis_cent, gueltig_von, erfasst_von)
VALUES
(100, 4500, '2026-01-01', '2026-03-15 09:00:00');
6. Abfragen mit kombinierten Zeitpunkten: as of valid time und as of transaction time
Eine bitemporale Abfrage nimmt typischerweise zwei Zeitpunkte entgegen statt einem: einen für die Gültigkeitszeit und einen für die Erfassungszeit. Damit lassen sich vier grundsätzlich verschiedene Fragen beantworten: der heute bekannte, heute gültige Wert, der heute bekannte, historisch gültige Wert, der damals bekannte, heute gültige Wert und der damals bekannte, damals gültige Wert, wie im Reproduktionsbeispiel oben.
Für ein reproduzierbares Reporting genügt es meist, beide Zeitpunkte auf denselben historischen Stichtag zu setzen: Was galt fachlich an diesem Tag, basierend auf dem Wissensstand genau dieses Tages. Das unterscheidet sich deutlich von einer reinen Gültigkeitsabfrage, die den heutigen Wissensstand über einen vergangenen Zeitraum liefern würde und damit rückwirkende Korrekturen unbemerkt einfließen ließe.
-- Bitemporale Abfrage: gültig am 2026-01-15, Wissensstand vom 2026-02-01
SELECT preis_cent
FROM preis_bitemporal
WHERE produkt_id = 100
AND gueltig_von <= '2026-01-15' AND gueltig_bis > '2026-01-15'
AND erfasst_von <= '2026-02-01' AND erfasst_bis > '2026-02-01';
7. Modellierungsmuster: getrennte Achsen kombinieren statt neu erfinden
Statt beide Achsen komplett von Hand zu bauen, lässt sich die Erfassungszeit häufig direkt über System-Versioning nach SQL:2011 abbilden, während die Gültigkeitszeit über eine zweite, von der Anwendung gepflegte Periode mit PERIOD FOR APPLICATION_TIME ergänzt wird, sofern das Datenbanksystem beide Konzepte unterstützt. IBM DB2 kombiniert beide Mechanismen bereits nativ zu echten bitemporalen Tabellen, bei denen die Datenbank die Erfassungszeit automatisch verwaltet und die Anwendung nur noch die Gültigkeitszeit setzen muss.
In Systemen ohne diese native Unterstützung, etwa in PostgreSQL oder MySQL, bleibt nur der manuelle Weg über explizite Zeitstempelspalten und diszipliniert eingehaltene Insert-statt-Update-Regeln, idealerweise gekapselt in einer eigenen Datenzugriffsschicht, damit kein Aufrufer versehentlich eine bitemporale Zeile direkt überschreibt.
8. Den Modellierungsaufwand realistisch einordnen
Bitemporale Modelle sind spürbar aufwendiger als einfache Zeitstempel: Jede Abfrage braucht vier statt zwei Zeitbedingungen, jede Schreiboperation muss als Insert statt als Update gedacht werden, und Reports müssen explizit festlegen, welche Kombination aus Gültigkeits- und Erfassungszeitpunkt sie eigentlich meinen. Für Teams ohne Erfahrung mit diesem Muster ist die Fehlerquote in den ersten Wochen entsprechend höher.
Der Aufwand lohnt sich vor allem dort, wo beide Fragen regelmäßig getrennt beantwortet werden müssen, etwa im Finanzwesen, im Vertragsmanagement oder in regulierten Branchen mit Prüfpflicht. Für gewöhnliche Geschäftsanwendungen, bei denen nur die aktuell gültige Version zählt und Korrekturen selten und unkritisch sind, ist eine vollständig bitemporale Modellierung in aller Regel überdimensioniert.
9. Typische Fehler bei der Einführung bitemporaler Modelle
Der häufigste Fehler ist die Verwechslung der beiden Achsen: Entwicklerinnen und Entwickler setzen aus Gewohnheit gueltig_von auf den aktuellen Zeitpunkt, obwohl die fachliche Gültigkeit eigentlich in der Vergangenheit oder Zukunft liegen sollte, und verwässern damit den gesamten Sinn der Trennung. Ein zweiter häufiger Fehler sind fehlende Constraints gegen sich überlappende Gültigkeitszeiträume innerhalb derselben Erfassungsperiode, was zu widersprüchlichen, gleichzeitig gültigen Werten führen kann.
Ein dritter Fehler ist, die Erfassungszeit versehentlich per UPDATE statt per Insert-Kette zu pflegen, wodurch die eigentlich unveränderliche Historie stillschweigend überschrieben wird. Wer bitemporale Tabellen einführt, sollte diese Regeln in einer eigenen Zugriffsschicht erzwingen, statt sich auf die Disziplin jedes einzelnen Aufrufers zu verlassen.
| Merkmal | Gültigkeitszeit (Valid Time) | Erfassungszeit (Transaction Time) | Wer setzt den Wert |
|---|---|---|---|
| Beschreibt | Wann ein Fakt fachlich gilt | Wann ein Fakt im System bekannt wurde | Unterschiedlich |
| Kann in der Zukunft liegen | Ja, z. B. künftige Preisänderung | Nein, immer der aktuelle Moment | Fachlogik vs. Datenbank |
| Nachträglich korrigierbar | Ja, über neue Zeile | Nein, gilt als unveränderlicher Fakt | Anwendung vs. Datenbank |
| Typischer Setzer | Fachanwendung oder Nutzerin | Datenbank oder Transaktionslogik automatisch | Getrennt gehalten |
| Genutzt für | Fachliche Zeitreise, Verträge, Preise | Audit, Reproduzierbarkeit, Compliance | Beide zusammen: Bitemporalität |
Mironsoft
Datenbank-Optimierung, Query-Tuning und Migrationen
SQL-Abfragen, die bei Wachstum immer langsamer werden?
Wir analysieren und optimieren SQL-Datenbanken unabhängig vom eingesetzten System, planen sichere Migrationen und Schema-Änderungen und bringen Teams Query-Optimierung praxisnah bei.
Query-Optimierung
Langsame Abfragen analysieren und mit Indizes und Explain-Plänen gezielt beschleunigen.
Migrations-Planung
Schema-Änderungen und Datenmigrationen sicher und ohne Downtime umsetzen.
Team-Schulung
SQL-Grundlagen und Performance-Denken praxisnah im Entwicklerteam verankern.
10. Zusammenfassung
Bitemporale Modellierung: das Wichtigste auf einen Blick
Valid Time
Beschreibt, wann ein Sachverhalt fachlich galt, gesetzt von der Anwendung.
Transaction Time
Beschreibt, wann ein Fakt im System bekannt wurde, automatisch und unveränderlich.
Praxisfall
Rückwirkende Preiskorrekturen bleiben mit beiden Achsen exakt nachvollziehbar.
Aufwand
Deutlich höher als einfache Zeitstempel, lohnt sich vor allem bei Prüfpflicht.