系统版本控制时态表是持久化表而非临时表,需单独授权历史表select权限,更新删除操作自动维护历史数据,禁止手动修改validto字段,清理历史数据应优先使用分区切换。

系统版本临时表不是临时表,别被名字误导
SQL Server 2016 的「系统版本控制时态表」(SYSTEM_VERSIONING = ON)和「本地/全局临时表」(#Temp / ##Temp)完全无关。前者是带历史追踪能力的永久表,后者是会话级或实例级的内存/磁盘暂存结构。你在存储过程中想用「系统版本表」,实际是要操作带 ValidFrom/ValidTo 的时态表——它不临时,也不在 tempdb 里,而是在你指定的用户数据库中持久存在。
在存储过程中查询系统版本表必须显式授权历史表 SELECT 权限
即使你对当前表(如 Employee)有 SELECT 权限,SQL Server 也不会自动授予对关联历史表(如 EmployeeHistory)的访问权。执行 SELECT * FROM Employee FOR SYSTEM_TIME ALL 或直接查 EmployeeHistory 时,会报错:The SELECT permission was denied on the object 'EmployeeHistory'。
- 必须单独执行:
GRANT SELECT ON dbo.EmployeeHistory TO [your_user_or_role] - 如果使用
EXECUTE AS切换上下文,权限检查仍基于调用者对历史表的实际权限,不是执行者身份 -
FOR SYSTEM_TIME查询走的是当前表的元数据路由,但底层仍需访问历史表,所以权限缺一不可
存储过程中更新/删除系统版本表,历史数据自动生成,但不能手动改 ValidTo
你不需要、也不能在存储过程里写 UPDATE EmployeeHistory SET ValidTo = ...。所有历史行版本由系统维护,任何对当前表的 UPDATE 或 DELETE 都会触发自动归档:
-
UPDATE Employee SET Position = 'Senior Dev' WHERE EmployeeID = 123→ 原行自动关窗(ValidTo被设为事务开始时间),新行开启(ValidFrom设为同一时间) -
DELETE FROM Employee WHERE EmployeeID = 456→ 行从当前表移出,ValidTo写入删除时刻 UTC 时间,进入历史表 - 若尝试在
UPDATE中显式赋值ValidTo,会报错:Cannot update GENERATED ALWAYS columns.
清理过期历史数据:别用 DELETE,优先用分区切换(SWITCH)
直接 DELETE FROM EmployeeHistory WHERE ValidTo 在大表上会锁表、阻塞查询、产生巨量日志。正确做法是用分区切换实现毫秒级“删除”:
- 历史表必须有以
ValidTo为分区列的聚集索引,且分区函数按时间范围切分(如每月一个分区) - 先创建空的占位表(结构与历史表一致,含相同分区方案),再执行:
ALTER TABLE EmployeeHistory SWITCH PARTITION 1 TO staging_table - 然后
DROP TABLE staging_table或TRUNCATE它——这一步不记日志、不锁全表、不影响SYSTEM_VERSIONING正常运行 - 注意:
SWITCH要求源表和目标表的约束、索引、压缩设置完全匹配,否则报错ALTER TABLE SWITCH statement failed
真正容易被忽略的是:分区切换只适用于历史表已提前按 ValidTo 建好分区架构的情况;如果你建表时没配分区,后续加分区要重建聚集索引,代价远高于初期规划。











