Temporal Tables & Time-Travel Queries
Track data history automatically and query "what did this look like last Tuesday?" with temporal tables.
๐ฏ What You'll Learn
- System-time vs application-time temporal data
- Building temporal tables in PostgreSQL with triggers
- Time-travel queries: "show me data as of date X"
- Application-time ranges for subscriptions and bookings
- Audit trails and compliance queries
โฐ What Are Temporal Tables?
Normal tables are like a whiteboard โ when you erase and rewrite, the old text is gone. Temporal tables are like a notebook โ every version of every row is preserved. You can flip back to any page (point in time) and see exactly what the data looked like.
Temporal Concepts
System-time vs application-time temporal data
-- Temporal tables track the HISTORY of every row
-- "What did this data look like last Tuesday at 3 PM?"
-- Two types of temporal data:
-- 1. SYSTEM-TIME (transaction time): auto-managed by DB
-- Records when the DB stored the data
-- 2. APPLICATION-TIME (valid time): managed by your app
-- Records when the data is valid in the real world
-- Example: Employee salary history
-- Without temporal: UPDATE overwrites the old salary โ gone forever
-- With temporal: every change is preserved a
...๐ PostgreSQL Implementation
PostgreSQL doesn't have built-in temporal tables (yet), but you can implement them with triggers that automatically archive old row versions to a history table.
๐ก Pro Tip โ Use 'infinity' for Current Rows
Set valid_to = 'infinity' for current (active) rows. This makes time-travel queries simple: WHERE valid_from <= timestamp AND valid_to > timestamp. No need to check for NULLs.
PostgreSQL Temporal Tables
Trigger-based versioning with history table
-- PostgreSQL: Manual temporal table implementation
-- (PostgreSQL doesn't have built-in temporal tables yet)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price DECIMAL(10,2) NOT NULL,
category VARCHAR(50),
-- Temporal columns
valid_from TIMESTAMP NOT NULL DEFAULT NOW(),
valid_to TIMESTAMP NOT NULL DEFAULT 'infinity',
-- Track who made the change
modified_by VARCHAR(100) DEFAULT current_user
);
-- History table (stores old versi
...๐ฐ๏ธ Time-Travel Queries
The whole point of temporal tables: asking "What did X look like at time T?" This is essential for debugging, auditing, regulatory compliance, and understanding how data evolved.
Time-Travel Queries
Query data as it existed at any past moment
-- TIME-TRAVEL QUERIES: query data as it was at any point in time
-- SQL Server syntax (built-in):
-- SELECT * FROM employees
-- FOR SYSTEM_TIME AS OF '2024-06-15 14:30:00';
-- PostgreSQL manual approach:
-- "What was the product price on June 15, 2024?"
SELECT * FROM (
-- Current rows valid at that time
SELECT * FROM products
WHERE valid_from <= '2024-06-15 14:30:00'
AND valid_to > '2024-06-15 14:30:00'
UNION ALL
-- Historical rows valid at that time
SELECT * FRO
...๐ Application-Time Ranges
Application-time tracks when data is valid in the real world (not when it was stored). Think insurance policies, hotel bookings, or subscription plans โ each has a defined validity period.
โ ๏ธ Common Mistake โ Overlapping Periods
Without an EXCLUDE constraint, you can accidentally insert overlapping subscriptions for the same customer. Use PostgreSQL's EXCLUDE USING gist with daterange to prevent this at the database level.
Application-Time Ranges
Subscriptions, coverage gaps, and overlap prevention
-- APPLICATION-TIME: track when data is valid in reality
-- Example: Insurance policies, subscriptions, room bookings
CREATE TABLE subscriptions (
id SERIAL PRIMARY KEY,
customer_id INT NOT NULL,
plan VARCHAR(50) NOT NULL,
monthly_price DECIMAL(8,2),
-- Application-time period
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
CHECK (valid_from < valid_to),
-- Prevent overlapping subscriptions per customer
EXCLUDE USING gist (
customer_id WITH =,
...๐ Audit & Compliance
Temporal tables give you a complete audit trail for free. Regulators can ask "what data did you have on March 15?" and you can answer with a single query.
Audit Trails
Change history, diff reports, and compliance queries
-- Temporal tables for compliance and auditing
-- "Show me every change to this employee's record"
SELECT
h.name,
h.salary,
h.department,
h.valid_from AS changed_at,
h.valid_to AS superseded_at,
h.modified_by
FROM products_history h
WHERE h.id = 42
ORDER BY h.valid_from;
-- "What changed between two dates?"
SELECT
COALESCE(old.id, new.id) AS product_id,
old.price AS old_price,
new.price AS new_price,
CASE
WHEN old.id IS NULL THEN 'ADDED'
...๐ Quick Reference
| Concept | Key Point |
|---|---|
| System-time | Auto-tracked by database (transaction time) |
| Application-time | Business validity period (your app manages) |
| AS OF | Point-in-time snapshot query |
| EXCLUDE USING gist | Prevent overlapping ranges |
| 'infinity' | Marks current/active rows |
๐ Lesson Complete!
You can now track data history and query any point in time. Next, explore full-text search and fuzzy matching!
Sign up for free to track which lessons you've completed and get learning reminders.