时态表修改需遵守系统版本控制规则:INSERT 必须省略或用 DEFAULT 填充 ValidFrom/ValidTo;UPDATE/DELETE 自动维护历史表;禁止 TRUNCATE、直接修改历史表及使用 INSTEAD OF 触发器。

时态表的数据修改和普通表基本一致,但关键限制是:不能直接写入或更新 ValidFrom 和 ValidTo 这类系统生成的时间段列——SQL Server 会自动管理它们的值,并在 UPDATE/DELETE 时生成历史快照。
INSERT 语句必须避开或显式用 DEFAULT 填充时间段列
如果你的时态表把 ValidFrom 和 ValidTo 设为 HIDDEN(推荐),那 INSERT 完全不用提这两个列:
INSERT INTO dbo.Employee (EmployeeID, Name, Position, Department) VALUES (1001, 'Alice', 'Engineer', 'Dev');
但如果列是可见的,又写了列名列表,就必须省略时间段列,否则报错 Cannot insert an explicit value into a GENERATED ALWAYS column;如果非得带上它们,只能写 DEFAULT:
- 带列名列表且含时间段列 → 必须写
DEFAULT, DEFAULT - 不带列名列表 → 所有列都要按顺序提供值,时间段列也必须是
DEFAULT - 用
SELECT INTO或INSERT ... SELECT时,同样不能 SELECT 出时间段列的显式值
UPDATE 和 DELETE 操作自动触发历史记录生成
执行 UPDATE 或 DELETE 时,你不需要、也不能干预时间戳逻辑。SQL Server 会在事务内自动完成两件事:
- 把原行从当前表移出,填入
ValidTo(设为事务开始时间)后存入历史表 - 在当前表中更新/插入新行,
ValidFrom设为同一时间点,ValidTo设为'9999-12-31'
注意:如果 UPDATE 只改了非主键列,它仍会生成历史版本;但如果 UPDATE 的 WHERE 条件没匹配到任何行,就不会触发任何历史写入——这点和普通表一致。
不能 TRUNCATE、不能直接改历史表、不能用 INSTEAD OF 触发器
这些操作被 SQL Server 明确禁止,尝试会立即报错:
-
TRUNCATE TABLE→ 报错Cannot truncate table 'X' because it is not supported for system-versioned temporal tables - 对历史表执行
INSERT/UPDATE/DELETE→ 报错Cannot modify a system-versioned temporal table's history table directly - 在当前表或历史表上建
INSTEAD OF触发器 → 创建失败,因为会破坏系统自动生成版本的机制
唯一允许的触发器是 AFTER 类型,且只允许建在当前表上。
分区切换可用于清理历史数据,但仅限历史表
想批量删掉旧历史(比如 3 年前的记录),别用 DELETE FROM ... WHERE ValidTo ——它会逐行锁、日志爆炸、还拖慢查询。正确做法是用 <code>SWITCH OUT:
- 历史表必须已按
ValidTo列做了分区(聚集索引也要包含ValidTo) - 只能对历史表执行
ALTER TABLE ... SWITCH PARTITION X TO staging_table - 当前表不支持
SWITCH OUT,哪怕 SYSTEM_VERSIONING = OFF 也不行
真正容易被忽略的是:分区方案必须提前规划。一旦历史表用默认方式创建,它的分区结构就固定了,后期加分区函数或拆分边界需要重建整个历史表——这在生产环境几乎不可行。

















