sql server 2016时态表需建表时显式定义period列和system_versioning,不可后期启用;insert/update/delete自动维护历史,查询必须用for system_time子句。

SQL Server 2016 的系统版本表(Temporal Table)不是“开启追踪开关”就能用的,它依赖表结构定义和 DML 行为自动存档——你不能手动改 ValidFrom 或 ValidTo,也不能直接删历史行;只要表建对、操作合规,历史就自然产生。
创建时必须显式声明 PERIOD 列和 SYSTEM_VERSIONING
系统版本表不是靠 ALTER 启用的“功能”,而是建表时就定型的结构。漏掉任何一项都会失败:
-
PERIOD FOR SYSTEM_TIME必须指定两个DATETIME2列,且这两列必须带GENERATED ALWAYS AS ROW START/END -
SYSTEM_VERSIONING = ON必须写在WITH子句里,否则只是普通带时间字段的表 - 如果想复用已有历史表,需提前建好同构表并用
HISTORY_TABLE = dbo.YourHistoryTable显式引用;否则系统会自动生成一个带_History后缀的表
错误示例:CREATE TABLE t (id INT, ts DATETIME2) WITH (SYSTEM_VERSIONING = ON) —— 缺少 PERIOD 定义,报错 Msg 13575。
INSERT/UPDATE/DELETE 的行为是自动且不可绕过的
所有修改都走当前表,历史由引擎在事务内同步写入历史表,无需额外代码:
-
INSERT:只影响当前表,ValidFrom设为事务开始时间,ValidTo设为'9999-12-31' -
UPDATE:旧值立刻封入历史表(ValidTo改为当前事务时间),新值写入当前表(ValidFrom为当前事务时间,ValidTo再次设为'9999-12-31') -
DELETE:不是真删,而是把整行从当前表移到历史表,并把该行的ValidTo改为事务开始时间
注意:MERGE 语句中每个分支(WHEN MATCHED / NOT MATCHED)触发的 UPDATE 或 DELETE,同样按上述规则生成历史记录。
查询历史必须用 FOR SYSTEM_TIME,不能直接 SELECT 历史表
历史表是内部维护结构,直接查它可能返回不一致或未提交的数据;正确方式是通过 FOR SYSTEM_TIME 子句让引擎做一致性快照:
-
AS OF '2025-01-01':重建某时刻的全表状态(当前 + 历史中ValidFrom ≤ time 的行) -
BETWEEN '2025-01-01' AND '2025-01-31':返回该时间段内**发生过变更**的所有行版本(含起止边界) -
CONTAINED IN ('2025-01-01', '2025-01-31'):只返回生命周期**完全落在该区间内**的行版本(不含边界)
常见坑:SELECT * FROM dbo.EmployeeHistory 看起来能拿到历史,但无法保证事务隔离级别,且不能跨当前/历史联合分析;而 SELECT * FROM dbo.Employee FOR SYSTEM_TIME ALL 才是安全、标准的历史全量视图。
HIDDEN 列会影响现有应用兼容性,但不改变底层逻辑
如果已有程序 SELECT * 或 INSERT 不带列名,直接加 PERIOD 列会破坏它们。此时应把 PERIOD 列设为 HIDDEN:
- 建表时加
HIDDEN关键字:[ValidFrom] DATETIME2 GENERATED ALWAYS AS ROW START HIDDEN - 这样
SELECT *不显示这些列,INSERT INTO t VALUES (...)也不需要提供它们(系统自动填) - 但查询时若需时间字段,仍要显式写进 SELECT 列表:
SELECT id, name, ValidFrom, ValidTo FROM t
真正容易被忽略的是:HIDDEN 只影响元数据可见性,不影响历史生成逻辑——哪怕列被隐藏,UPDATE 依然会封存旧版本到历史表,FOR SYSTEM_TIME 查询也完全不受影响。











