Another new feature in SQL 2016 is the Temporal Table (or System Versioning, as its referred to in the documentation). It allows a table to be versioned, in terms of data, and for queries to access rows of the table as they were at some earlier point in time,
I’m quite excited about this, because while we’ve always been able to do this manually, with triggers or CDC or CT, it’s been anything but trivial. I remember trying to implement a form of temporal tables back in SQL 2000, using triggers, and it was an absolute pain in the neck.
So how does it work? Let’s start with a normal un-versioned table.
CREATE TABLE dbo.Stock ( StockReferenceID INT IDENTITY(1, 1) NOT NULL, IssueID INT NULL, Condition VARCHAR(10) NULL, AvailableQty SMALLINT NULL, Price NUMERIC(8, 2) NULL, PRIMARY KEY CLUSTERED (StockReferenceID ASC) );
To make that a temporal table, we need to add two columns, a row start date and a row end date.
ALTER TABLE Stock ADD PERIOD FOR SYSTEM_TIME (RowStartDate, RowEndDate), RowStartDate DATETIME2 GENERATED ALWAYS AS ROW START HIDDEN NOT NULL DEFAULT GETDATE(), RowEndDate DATETIME2 GENERATED ALWAYS AS ROW END HIDDEN NOT NULL DEFAULT CONVERT(DATETIME2, '9999-12-31 23:59:59.99999999');
It’s a little complicated. From my, admittedly limited, testing, the NOT NULL and the DEFAULT are required. The start time’s default needs to be GETDATE() and the end time’s default needs to be the max value of the data type used.
Hidden is an interesting property, it means that the columns won’t appear if SELECT * FROM Stock… is run. The only way to see the column values is to explicitly state them in the SELECT clause.
I wonder if that property will be available for other columns in the future. It would be nice to be able to mark large blob columns as HIDDEN so that SELECT * doesn’t end up pulling many MB per row back.
That ALTER adds the row’s two time stamps. To enable the versioning then just requires
ALTER TABLE Stock SET (SYSTEM_VERSIONING = ON);
Once that’s done, the table gains a second, linked, table that contains the history of the rows.
421576540 is the object_id for the Stock table. If is also possible to specify the name for the history table in the ALTER TABLE statement, if preferred.
The history table can be queried directly. The start and end times aren’t hidden in this one.
Or, the temporal table can be queried with a new clause added to the FROM, FOR SYSTEM TIME… Full details at https://msdn.microsoft.com/en-us/library/mt591018.aspx
The one thing that does need mentioning. This is not an audit solution. If the table is altered, history can be lost. If the table is dropped, history is definitely lost. Auditing requirements are such that audit records should survive both. Use this for historical views of how the data looked, and if you need an audit as well, look at something like SQLAudit.