Temporal / System-Versioned Tables
Tables that automatically track the full history of row changes, enabling queries against data as it existed at a past point in time.
temporal table system-versioned table bi-temporal time travel AS OF FOR SYSTEM_TIME Flashback history table
temporal history time travel versioning audit point-in-time
| Database | Status | Syntax Family | Min Version |
|---|---|---|---|
| PostgreSQL | none | - | - |
| MySQL | none | - | - |
| MariaDB | full | sql-2011 | 10.3.4 |
| Oracle | full | flashback | - |
| SQL Server | full | sql-2011 | 2016 |
| SQLite | none | - | - |
| Redshift | none | - | - |
| DB2 | full | sql-2011 | 10.1 |
| Snowflake | full | time-travel | - |
| BigQuery | full | time-travel | - |
Compare Databases
Database Details
PostgreSQL
none
- No built-in temporal tables. The temporal_tables extension provides a trigger-based implementation of system-period tables.
- pgaudit is a separate concern — audit logging, not versioned queries.
MySQL
none
- Not supported. Trigger-based audit tables are the usual substitute.
Docs: CREATE TABLE, SELECT
MariaDB
full
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(100),
salary DECIMAL(10,2),
row_start DATETIME(6) GENERATED ALWAYS AS ROW START,
row_end DATETIME(6) GENERATED ALWAYS AS ROW END,
PERIOD FOR SYSTEM_TIME(row_start, row_end)
) WITH SYSTEM VERSIONING;
-- Query current data
SELECT * FROM employees;
-- Query as of a specific point in time
SELECT * FROM employees
FOR SYSTEM_TIME AS OF '2023-01-01 00:00:00';
-- Query all versions including history
SELECT * FROM employees
FOR SYSTEM_TIME ALL;- Added in 10.3.4, one of the first open-source implementations of SQL:2011.
- History lives in the same table, optionally partitioned out via PARTITION BY SYSTEM_TIME.
- FOR SYSTEM_TIME accepts AS OF, BETWEEN ... AND ..., FROM ... TO ..., and ALL.
- Existing tables can be converted with ALTER TABLE ... ADD SYSTEM VERSIONING.
Docs: System-Versioned Tables
Oracle
full
-- Enable Flashback Data Archive on a table
ALTER TABLE employees FLASHBACK ARCHIVE;
-- Query as of a specific timestamp
SELECT * FROM employees
AS OF TIMESTAMP TO_TIMESTAMP('2023-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS');
-- Query as of a specific System Change Number (SCN)
SELECT * FROM employees
AS OF SCN 1234567;
-- Query across a time range
SELECT * FROM employees
VERSIONS BETWEEN TIMESTAMP
TO_TIMESTAMP('2023-01-01', 'YYYY-MM-DD')
AND TO_TIMESTAMP('2023-06-01', 'YYYY-MM-DD');- Syntax predates SQL:2011. AS OF TIMESTAMP / AS OF SCN reads a single point in time; VERSIONS BETWEEN returns every row version across a range.
- Flashback Data Archive must be enabled (ALTER TABLE ... FLASHBACK ARCHIVE) and history lives in a configurable separate tablespace with a retention period.
SQL Server
full
CREATE TABLE employees (
id INT PRIMARY KEY,
name NVARCHAR(100),
salary DECIMAL(10,2),
valid_from DATETIME2 GENERATED ALWAYS AS ROW START,
valid_to DATETIME2 GENERATED ALWAYS AS ROW END,
PERIOD FOR SYSTEM_TIME(valid_from, valid_to)
) WITH (SYSTEM_VERSIONING = ON
(HISTORY_TABLE = dbo.employees_history));
-- Query as of a specific point in time
SELECT * FROM employees
FOR SYSTEM_TIME AS OF '2023-01-01T00:00:00';
-- Query across a time range
SELECT * FROM employees
FOR SYSTEM_TIME BETWEEN '2023-01-01' AND '2023-06-01';- Added in 2016; also on Azure SQL Database.
- History is kept in a separate (explicit or implicit) HISTORY_TABLE. Queries with FOR SYSTEM_TIME are routed to it automatically.
SQLite
none
- Not supported. Track history in application code or with triggers.
Redshift
none
- Not supported. Maintain a parallel history table via ETL or stored procedures.
Docs: CREATE TABLE
DB2
full
CREATE TABLE employees (
id INT NOT NULL,
name VARCHAR(100),
salary DECIMAL(10,2),
row_begin TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW BEGIN,
row_end TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW END,
trans_start TIMESTAMP(12) GENERATED ALWAYS AS TRANSACTION START ID,
PERIOD SYSTEM_TIME(row_begin, row_end)
) IN userspace1;
ALTER TABLE employees ADD VERSIONING
USE HISTORY TABLE employees_history;
-- Query as of a specific time
SELECT * FROM employees
FOR SYSTEM_TIME AS OF TIMESTAMP '2023-01-01 00:00:00';- Added in 10.1.
- Supports system-time, application-time, and fully bi-temporal tables combining both.
- History table is created explicitly and attached with ALTER TABLE ... ADD VERSIONING USE HISTORY TABLE ...
Docs: Temporal tables
Snowflake
full
-- Query as of a specific timestamp
SELECT * FROM employees
AT(TIMESTAMP => '2023-01-01 00:00:00'::TIMESTAMP_TZ);
-- Query as of a relative offset (24 hours ago)
SELECT * FROM employees
AT(OFFSET => -86400);
-- Query before a specific statement ID
SELECT * FROM employees
BEFORE(STATEMENT => '8e5d0ca9-005e-44e6-b858-a8f5b37c5726');
-- Restore a dropped table
UNDROP TABLE employees;- Time Travel window: 1 day on Standard, up to 90 days on Enterprise.
- AT / BEFORE syntax, not SQL:2011. UNDROP TABLE recovers a dropped table within the window.
- Fail-safe extends recovery by 7 days after Time Travel expires, but is only accessible through Snowflake support.
BigQuery
full
-- Query as of 1 day ago using SQL:2011 syntax
SELECT * FROM mydataset.employees
FOR SYSTEM_TIME AS OF
TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY);
-- Query as of a specific timestamp
SELECT * FROM mydataset.employees
FOR SYSTEM_TIME AS OF '2023-01-01 00:00:00 UTC';- FOR SYSTEM_TIME AS OF is enabled on every standard table with no setup.
- Retention is 2–7 days per table (default 7).
- RESTORE TABLE copies historical data back into a new or existing table.