系统版本控制表中DELETE不是真正删除,而是将行移入历史表并更新ValidTo为删除时刻;需手动查历史表(用ValidTo过滤)并排除Generated Always列后重新插入。

不能直接“恢复已删除数据”,系统版本控制表不提供类似 FLASHBACK 或回收站的逆向操作;它只保留历史快照,你需要手动查询历史表并重新插入。
为什么 DELETE 不是真正删除?
对系统版本控制表执行 DELETE 时,SQL Server 实际上是把该行从当前表移入历史表,并自动设置其 ValidTo 值为删除发生时刻(UTC 时间)。这行数据依然完整存在于历史表中,只是不再出现在 SELECT * FROM [CurrentTable] 的默认结果里。
- 历史表结构与当前表完全一致(含主键、索引等),只是多了两列:
ValidFrom和ValidTo - 被删行在历史表中的
ValidTo是精确到datetime2(7)的时间戳,ValidFrom是它上次生效的起始时间 - 如果原表启用了
SYSTEM_VERSIONING = ON,你无法用TRUNCATE清空当前表——会报错Msg 13539
如何定位并取回某条被删的数据?
核心是查历史表 + 精确时间过滤。假设你的时态表叫 dbo.Employee,历史表为 dbo.EmployeeHistory,且你知道删除大约发生在 '2026-09-28 14:30' 左右:
SELECT * FROM dbo.EmployeeHistory WHERE EmployeeID = 123 AND ValidTo >= '2026-09-28 14:29:00' AND ValidTo <= '2026-09-28 14:31:00';
- 别用
ValidFrom过滤删除动作——它记录的是“开始有效”时间,不是删除时间 - 用
ValidTo匹配,因为删除会让该值从'9999-12-31'变为实际终止时间 - 如果不确定时间,可先查
MAX(ValidTo)找最近一批关闭的行,再缩小范围
取回数据时要注意什么?
不能直接 INSERT INTO dbo.Employee SELECT ... FROM History,因为 ValidFrom 和 ValidTo 是 GENERATED ALWAYS 列,不允许显式插入:
INSERT INTO dbo.Employee (EmployeeID, Name, Position, Department, Address, AnnualSalary) SELECT EmployeeID, Name, Position, Department, Address, AnnualSalary FROM dbo.EmployeeHistory WHERE EmployeeID = 123 AND ValidTo = '2026-09-28 14:30:22.1234567';
- 必须排除
ValidFrom和ValidTo列,否则报错Msg 13537 - 插入后,新行的
ValidFrom会自动设为当前 UTC 时间,ValidTo设为'9999-12-31' - 如果原行有唯一约束冲突(比如重复
EmployeeID),需先UPDATE或处理业务逻辑,不能靠“覆盖”解决
最易被忽略的一点:历史表本身没有主键约束(即使当前表有),SQL Server 不会自动在历史表上创建主键或唯一索引。如果你依赖 EmployeeID + ValidFrom 组合做去重或关联,得自己加索引,否则大表查询会明显变慢。

















