February 9th, 2026
0 reactions

Time Travel in Azure SQL with Temporal Tables

Jerry Nixon
Principal Program Manager

Applications often need to know what data looked like before. Who changed it, when it changed, and what the previous values were. Rebuilding that history in application code is tedious and error prone. This is especially valuable when exposing a database to an AI agent through MCP servers like SQL MCP Server, where information discovery matters.

Learn more about SQL MCP Server at https://aka.ms/sql/mcp

Azure SQL includes a built in feature that tracks row history automatically. Temporal tables let the database keep a full change history without triggers, audit tables, or custom logic.

Working demo https://gist.github.com

What We Are Building

A table that automatically tracks every change over time.

Step 1: Create the Table

A temporal table requires two datetime columns that define the validity period. Azure SQL manages these automatically when you enable system versioning.

CREATE TABLE dbo.Todos
(
    Id        INT IDENTITY(1,1) PRIMARY KEY,
    Title     NVARCHAR(200) NOT NULL,
    State     NVARCHAR(20)  NOT NULL DEFAULT 'pending',

    ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START,
    ValidTo   DATETIME2 GENERATED ALWAYS AS ROW END,
    PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH (
    SYSTEM_VERSIONING = ON
    (
        HISTORY_TABLE = dbo.TodosHistory
    )
);
GO

INSERT INTO dbo.Todos (Title, State) VALUES
    ('Buy groceries', 'pending'),
    ('Walk the dog',  'pending');
GO

This table is created as a system-versioned temporal table. The ValidFrom and ValidTo columns define the period during which each row version is valid, and Azure SQL maintains them automatically. With SYSTEM_VERSIONING = ON, every UPDATE and DELETE causes the previous version of the row to be written to the history table. By specifying HISTORY_TABLE = dbo.TodosHistory, you control the name and schema of the history table, while Azure SQL continues to manage its contents and lifecycle. If you do not provide a history table, Azure SQL creates one for you automatically.

Step 2: Update Data Normally

No special syntax is required. Changes are tracked automatically.

UPDATE dbo.Todos
SET State = 'completed'
WHERE Title = 'Walk the dog';
GO

Each update creates a historical version of the row.

Step 3: Query Current Data

SELECT * FROM dbo.Todos;

Result

Id Title State ValidFrom ValidTo
1 Buy groceries pending current 9999-12-31
2 Walk the dog completed current 9999-12-31

Step 4: Query Historical Data

Use FOR SYSTEM_TIME to query past versions.

All Versions

SELECT *
FROM dbo.Todos
FOR SYSTEM_TIME ALL
ORDER BY Id, ValidFrom;

Result

Id Title State ValidFrom ValidTo
2 Walk the dog pending earlier update time
2 Walk the dog completed update time 9999-12-31

As Of a Point in Time

DECLARE @time DATETIME2 = DATEADD(MINUTE, -1, SYSUTCDATETIME());

SELECT *
FROM dbo.Todos
FOR SYSTEM_TIME AS OF @time;

This returns the table exactly as it existed at that moment.

Why Use Temporal Tables?

  • History is enforced by the database
  • No triggers or audit tables to maintain
  • All updates and deletes are captured
  • Application code stays simple
  • Works automatically with backups and restores
  • Allows reversible operations
  • Gives AI and automation traceability

Temporal tables move data history into the database engine itself. Once enabled, every change becomes observable, queryable, and reliable without extra code.

Author

Jerry Nixon
Principal Program Manager

SQL Server Developer Experience Program Manager for Data API builder.

3 comments

Sort by :
  • Daniel Flöijer

    Any plans to bring this to SQL Server?

    • Michael Taylor

      Yes, SQL Server had this added in SQL 2016 so about a decade now.

      While this is a great way to add auditing and it is easy to do, there are costs that should be evaluated first. There are also certain restrictions, so it won't work for all situations.

      - The table must have a PK. This is needed so the temporal table can match the history rows back to the original row. So you cannot use it on xref tables unless you also define a PK, even if you don't need one.
      - All columns are audited. While this may...

      Read more
    • Vaclav Elias

      SQL Server 2016+ if I am correct.