System-versionierte temporale Tabellen: automatische Historie nach SQL:2011
AI generated
SELECT
JOIN
SQL / Temporal Data
System-versionierte temporale Tabellen
automatische Historie nach dem SQL:2011-Standard

Wer den Zustand einer Zeile zu einem beliebigen Zeitpunkt in der Vergangenheit rekonstruieren muss, baut klassischerweise eine eigene Audit-Trail-Tabelle mit Triggern oder Anwendungscode. Der SQL:2011-Standard definiert dafür eine robustere Alternative: system-versionierte Tabellen, bei denen die Datenbank selbst bei jedem UPDATE und DELETE automatisch eine vollständige Historie mit gültigen Zeiträumen pflegt, ganz ohne zusätzlichen Anwendungscode. SQL Server und MariaDB implementieren dieses Konzept bereits produktionsreif, mit eigener Syntax für Zeitreise-Abfragen. Dieser Artikel zeigt, wie die Technik konkret funktioniert, wo sie sich von handgebauten Audit-Trails unterscheidet und wo ihre Grenzen liegen.

11 Min. Lesezeit System-Versioning · SQL:2011 SQL Server · MariaDB im Vergleich

1. Wie System-Versioning automatisch eine History-Tabelle pflegt

Bei einer system-versionierten Tabelle erhält jede Zeile zwei zusätzliche, von der Datenbank verwaltete Zeitstempel-Spalten: einen Beginn- und einen Ende-Zeitpunkt der Gültigkeit aus Sicht des Systems. Führt eine Anwendung ein UPDATE oder DELETE aus, schreibt die Engine automatisch und innerhalb derselben Transaktion die vorherige Version der Zeile mit geschlossenem Gültigkeitszeitraum in eine Historientabelle, bevor die aktuelle Tabelle die neue Version erhält. Für die Anwendung ändert sich am eigentlichen SQL-Statement dabei nichts.

Der entscheidende Unterschied zu klassischen Zeitstempel-Spalten wie updated_at liegt in der Vollständigkeit: Es gibt keinen Codepfad, über den eine Änderung an der History vorbeigeschrieben werden kann, weil die Logik direkt im Storage Layer der Datenbank verankert ist. Selbst ein Bulk-Update über tausend Zeilen erzeugt zuverlässig tausend History-Einträge, ohne dass ein einziger Trigger dafür manuell gepflegt werden müsste.


-- SQL Server: system-versionierte Tabelle anlegen
CREATE TABLE dbo.vertrag (
    vertrag_id   INT PRIMARY KEY,
    kunde_id     INT NOT NULL,
    status       VARCHAR(20) NOT NULL,
    gueltig_von  DATETIME2 GENERATED ALWAYS AS ROW START,
    gueltig_bis  DATETIME2 GENERATED ALWAYS AS ROW END,
    PERIOD FOR SYSTEM_TIME (gueltig_von, gueltig_bis)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.vertrag_history));

2. Der Unterschied zu manuell gebauten Audit-Trail-Tabellen

Ein klassischer Audit-Trail entsteht meist über einen Trigger, der bei UPDATE und DELETE die alte Zeile in eine separate Log-Tabelle kopiert, oder über Anwendungscode, der diese Aufgabe im ORM übernimmt. Beide Varianten funktionieren, solange jeder Schreibpfad diszipliniert durch dieselbe Schicht läuft. Sobald jemand ein direktes SQL-Statement gegen die Datenbank absetzt, einen Migrationslauf ohne ORM-Hooks ausführt oder ein Trigger versehentlich deaktiviert wird, entstehen stille Lücken in der Historie, die oft erst auffallen, wenn sie bereits gebraucht würde.

System-Versioning eliminiert genau diese Fehlerklasse, weil die Historienpflege nicht an Anwendungslogik gebunden ist, sondern an das Schreiben der Zeile selbst. Der Preis dafür ist weniger Flexibilität: Wer zusätzliche Metadaten wie den Verursacher einer Änderung mitschreiben will, muss dafür weiterhin eigene Spalten und eigene Logik ergänzen, denn das reine System-Versioning kennt nur den Zeitpunkt der Änderung, nicht deren Urheber.

3. System-Versioned Temporal Tables in SQL Server im Detail

SQL Server unterstützt System-Versioned Temporal Tables seit Version 2016. Die beiden generierten Spalten für Beginn und Ende der Gültigkeit werden über PERIOD FOR SYSTEM_TIME deklariert, und die Option SYSTEM_VERSIONING = ON verknüpft die Basistabelle mit einer eigenständigen Historientabelle. Diese Historientabelle lässt sich explizit benennen, wie im Beispiel oben, oder von SQL Server automatisch generieren lassen, inklusive eines passenden Clustered-Columnstore-Index für effiziente Komprimierung großer Historienbestände.

Über die Option HISTORY_RETENTION_PERIOD lässt sich außerdem festlegen, wie lange Historieneinträge aufbewahrt werden, bevor SQL Server sie automatisch über einen Hintergrundjob löscht. Das ist besonders bei Tabellen mit hoher Änderungsfrequenz relevant, denn ohne eine solche Grenze wächst die Historientabelle unbegrenzt und kann die eigentliche Nutztabelle nach wenigen Monaten um ein Vielfaches übersteigen.

4. System-Versioned Tables in MariaDB

MariaDB implementiert System-Versioning seit Version 10.3 mit der Klausel WITH SYSTEM VERSIONING direkt am CREATE TABLE-Statement. Anders als SQL Server legt MariaDB standardmäßig keine separate Historientabelle an, sondern hält alte und aktuelle Zeilen in derselben Tabelle, unterscheidbar über die intern verwalteten Systemspalten row_start und row_end. Abfragen ohne explizite Zeitangabe sehen automatisch nur die aktuell gültigen Zeilen, als wäre die Historie gar nicht vorhanden.

Für die Verwaltung des Speicherwachstums bietet MariaDB System-Versioning Partitions an, mit denen sich historische Zeilen nach Alter in separate Partitionen verschieben und über PARTITION ... HISTORY automatisiert wieder verwerfen lassen. Das entspricht funktional der Retention-Period von SQL Server, nur über den etablierten Partitionierungsmechanismus statt über eine dedizierte Konfigurationsoption.


-- MariaDB: system-versionierte Tabelle anlegen
CREATE TABLE vertrag (
    vertrag_id INT PRIMARY KEY,
    kunde_id   INT NOT NULL,
    status     VARCHAR(20) NOT NULL
) WITH SYSTEM VERSIONING;

-- Aktuelle Zeile ändern, MariaDB schreibt die alte Version automatisch fort
UPDATE vertrag SET status = 'gekuendigt' WHERE vertrag_id = 42;

5. Historische Daten mit FOR SYSTEM_TIME abfragen

Beide Systeme verwenden dieselbe standardisierte Syntax für Zeitreise-Abfragen: FOR SYSTEM_TIME AS OF liefert den Zustand zu einem exakten Zeitpunkt, FOR SYSTEM_TIME BETWEEN und FOR SYSTEM_TIME FROM TO liefern alle Versionen innerhalb eines Intervalls mit leicht unterschiedlicher Grenzbehandlung, und FOR SYSTEM_TIME ALL liefert die komplette Historie einer Zeile inklusive aller Zwischenversionen.

Für die Praxis bedeutet das: Statt eine eigene Rekonstruktionslogik über mehrere JOINs gegen eine Historientabelle zu schreiben, genügt eine einzige Zeitangabe im FROM-Teil der Abfrage, und die Datenbank übernimmt die komplette Auswahl der jeweils passenden Version automatisch, auch über Tabellengrenzen hinweg in JOINs mit weiteren system-versionierten Tabellen.


-- Zustand des Vertrags am Stichtag 2026-03-01, 12:00 Uhr rekonstruieren
SELECT vertrag_id, status
FROM vertrag
FOR SYSTEM_TIME AS OF '2026-03-01 12:00:00'
WHERE vertrag_id = 42;

-- Alle Statusänderungen eines Vertrags im ersten Quartal auflisten
SELECT vertrag_id, status, gueltig_von, gueltig_bis
FROM vertrag
FOR SYSTEM_TIME BETWEEN '2026-01-01' AND '2026-03-31'
WHERE vertrag_id = 42
ORDER BY gueltig_von;

6. Verfügbarkeitslücken: welche Datenbanken system-versionierte Tabellen unterstützen

PostgreSQL besitzt bis heute keine native Implementierung von System-Versioning nach SQL:2011. Wer diese Funktionalität dort braucht, greift entweder auf die Extension temporal_tables zurück, die über Trigger eine ähnliche History-Tabelle nachbildet, oder baut die Lösung von Hand mit Range-Typen und Exclusion Constraints nach. Auch reines MySQL, ohne den MariaDB-Fork, bietet keine eingebaute Unterstützung.

IBM DB2 gehörte historisch zu den ersten Systemen mit vollständiger SQL:2011-Unterstützung und deckt sowohl System-Period- als auch Application-Period-Tabellen ab. Oracle bietet mit Flashback Data Archive ein funktional ähnliches, aber syntaktisch eigenständiges Konzept, das ebenfalls automatisch Historie pflegt, jedoch nicht über die genormte FOR SYSTEM_TIME-Syntax abgefragt wird, sondern über eigene AS OF-Klauseln.

7. Speicherwachstum und Performance-Auswirkungen der History-Tabelle

Jede Änderung an einer system-versionierten Zeile erzeugt einen zusätzlichen Schreibvorgang in die Historie, zusätzlich zum eigentlichen UPDATE. Bei Tabellen mit sehr hoher Änderungsfrequenz, etwa Statuswechseln in Warteschlangen oder häufig aktualisierten Zählern, kann sich dieser doppelte Schreibaufwand spürbar auf die Latenz einzelner Transaktionen auswirken und sollte vor dem produktiven Einsatz mit realistischer Last gemessen werden.

Für lesende Zugriffe auf die Historie lohnt sich ein eigener Index auf den Zeitraumsspalten, da sonst jede Zeitreise-Abfrage einen vollständigen Tabellenscan der Historientabelle auslöst. In Kombination mit einer klaren Retention-Strategie, sei es über HISTORY_RETENTION_PERIOD in SQL Server oder History-Partitionen in MariaDB, bleibt die Historientabelle auch bei Millionen von Änderungen pro Jahr beherrschbar.

8. Was System-Versioning bewusst nicht abdeckt

System-Versioning kennt nur eine einzige Zeitachse, nämlich wann eine Zeile aus Sicht der Datenbank gültig war. Wann ein Sachverhalt fachlich, also aus Geschäftssicht, gültig war oder sein wird, bildet der Mechanismus nicht ab. Für eine rückwirkend korrigierte Rechnung etwa unterscheidet System-Versioning nicht zwischen dem Zeitpunkt der Korrektur im System und dem Zeitpunkt, ab dem die korrigierte Angabe fachlich gelten sollte. Wer diese Unterscheidung braucht, benötigt zusätzlich eine bitemporale Modellierung.

Auch Schema-Änderungen an der Basistabelle verdienen besondere Aufmerksamkeit: Eine neue Spalte muss in SQL Server in der Regel auch in der Historientabelle nachgezogen werden, und während die Versionierung aktiv ist, sind bestimmte DDL-Operationen eingeschränkt oder erfordern ein vorübergehendes Abschalten von SYSTEM_VERSIONING, was die History-Kette an dieser Stelle unterbricht und dokumentiert werden sollte.

9. Praxisempfehlung: wann sich System-Versioning lohnt

System-Versioning zahlt sich vor allem dort aus, wo eine lückenlose Nachvollziehbarkeit gesetzlich gefordert ist, etwa bei Vertrags-, Preis- oder Statusänderungen in regulierten Branchen, oder wo Entwickler regelmäßig rekonstruieren müssen, wie ein bestimmter Datensatz vor einem fehlerhaften Deployment aussah. Die Technik ersetzt in diesen Fällen eine ganze Kategorie von selbst geschriebenem, fehleranfälligem Trigger-Code durch eine geprüfte Datenbankfunktion.

Weniger sinnvoll ist der Einsatz bei Tabellen mit extrem hoher Schreiblast ohne echten Auditbedarf, da der zusätzliche Schreibaufwand dort reinen Overhead darstellt, sowie überall dort, wo tatsächlich Bitemporalität statt reiner Systemhistorie gebraucht wird. Für solche Anforderungen ist eine bewusst bitemporale Modellierung mit getrennten Zeitachsen für Gültigkeit und Erfassung die passendere Lösung.

Merkmal SQL Server MariaDB Manueller Audit-Trail
Historientabelle Separates, benanntes Objekt Meist dieselbe Tabelle mit Sichtbarkeitsfenster Muss selbst modelliert werden
Konsistenz bei Bulk-Updates Garantiert durch die Engine Garantiert durch die Engine Abhängig von Trigger-Abdeckung
Zeitreise-Abfrage FOR SYSTEM_TIME FOR SYSTEM_TIME Eigene JOIN-Logik nötig
Erfassung des Verursachers Nicht enthalten Nicht enthalten Frei modellierbar
Verfügbar seit SQL Server 2016 MariaDB 10.3 Datenbankunabhängig, jederzeit

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

System-versionierte Tabellen: das Wichtigste auf einen Blick

Automatik

Die Datenbank pflegt History-Zeilen selbst, ohne Trigger oder eigenen Anwendungscode.

SQL Server

PERIOD FOR SYSTEM_TIME plus separate Historientabelle, produktionsreif seit Version 2016.

MariaDB

WITH SYSTEM VERSIONING seit Version 10.3, Historie meist direkt in derselben Tabelle.

Grenze

Nur eine Zeitachse: für echte Bitemporalität ist ein zusätzliches Modell nötig.

11. FAQ: System-versionierte Tabellen: das Wichtigste auf einen Blick

1Was bedeutet System-Versioning bei Tabellen konkret?
Die Datenbank pflegt bei jeder Änderung einer Zeile automatisch eine vollständige Historie mit gültigen Zeiträumen, ohne dass dafür Trigger oder Anwendungscode geschrieben werden müssen. Das geschieht direkt im Storage Layer bei jedem UPDATE und DELETE.
2Wo speichert SQL Server die historischen Zeilen?
Standardmäßig in einer separaten, mit der Basistabelle verknüpften Historientabelle, die entweder explizit benannt oder von SQL Server automatisch mit einem passenden Columnstore-Index angelegt wird.
3Wie unterscheidet sich MariaDB im Speicherort der Historie von SQL Server?
MariaDB hält alte und aktuelle Zeilen in der Regel in derselben Tabelle und unterscheidet sie über interne Systemspalten. SQL Server nutzt dafür ein separates Datenbankobjekt.
4Kann ich mit FOR SYSTEM_TIME auch gelöschte Zeilen sehen?
Ja. Eine gelöschte Zeile bleibt in der Historie erhalten und lässt sich über FOR SYSTEM_TIME AS OF mit einem Zeitpunkt vor dem Löschen oder über FOR SYSTEM_TIME ALL weiterhin abfragen.
5Unterstützt PostgreSQL system-versionierte Tabellen nativ?
Nein, bis heute nicht. Für vergleichbare Funktionalität kommen Extensions wie temporal_tables oder eine manuelle Umsetzung mit Range-Typen und Exclusion Constraints zum Einsatz.
6Erfasst System-Versioning automatisch, wer eine Änderung vorgenommen hat?
Nein, der Mechanismus kennt nur den Zeitpunkt der Änderung. Wer den Verursacher mitschreiben möchte, muss dafür weiterhin eine eigene Spalte und eigene Anwendungslogik ergänzen.
7Wie wirkt sich System-Versioning auf die Schreibperformance aus?
Jede Änderung erzeugt einen zusätzlichen Schreibvorgang in die Historie. Bei sehr hoher Änderungsfrequenz sollte dieser Zusatzaufwand vor dem produktiven Einsatz mit realistischer Last gemessen werden.
8Was passiert mit der Historie, wenn ich eine Spalte zur Basistabelle hinzufüge?
Die neue Spalte muss in SQL Server meist auch in der Historientabelle nachgezogen werden, und bestimmte DDL-Operationen sind während aktiver Versionierung eingeschränkt oder erfordern ein temporäres Abschalten der Funktion.
9Ist System-Versioning dasselbe wie Bitemporalität?
Nein. System-Versioning kennt nur eine Zeitachse, nämlich wann eine Zeile aus Sicht der Datenbank gültig war. Wann ein Sachverhalt fachlich gültig war, bildet es nicht ab, dafür ist ein bitemporales Modell nötig.
10Wie begrenze ich das Wachstum der History-Tabelle sinnvoll?
Über eine Retention-Strategie: HISTORY_RETENTION_PERIOD in SQL Server oder History-Partitionen in MariaDB löschen alte Einträge automatisch nach einer festgelegten Frist und halten die Tabelle beherrschbar.