Microsoft在SQL Server 2016中发布了一个新功能——时态表(系统版本控制临时表)。

一、  原理与创建

首先说明SQL服务器和Oracle之间的一个误解,Temporal Tables实际上与Oracle 12c Release 1中的新功能:Temporal Validity而非闪回技术更相似,更多信息参考此处。闪回基于undo或闪回日志文件,而不是直接基于表。

每个系统版本控制表都分配有一个历史记录表,但该表对用户完全透明,除非他们想要通过创建附加索引或选择不同的存储选项来优化工作负载性能或存储空间。

下图说明了使用系统版本控制临时表的典型工作流:

1.   为何使用temporal

  • 能够使数据库“回到某个时间”,查看当时的数据情况,因此可以恢复一定时间内dml误操作
  • 类似DML审计(INSERT,UPDATE,DELETE或MERGE),因为跟踪了所有数据的变化。

2.   工作原理

当创建时态表或将普通表Alter为时态表时,有2个表将会被创建:

  • System-Versioned Table:记录当前值的当前表
  • History Table,包含每行记录的所有历史值

原理很简单:添加“开始”和“结束时间”列以使每个记录具有有效期。

示例中,创建了一个表'Animals'来存储动物。

CREATE TABLE [dbo].[Animals]
([AnimalId] [int] IDENTITY(1,1) NOT NULL,[Name] [varchar](200) NOT NULL,[Genus Species] [varchar](200) NOT NULL,[Number]  [int] NOT NULL,CONSTRAINT [PK_Animals] PRIMARY KEY CLUSTERED ([AnimalId] ASC),/*Temporal: Define the Period*/[StartDate] [datetime2](0) GENERATED ALWAYS AS ROW START NOT NULL,[EndDate]  [datetime2](0) GENERATED ALWAYS AS ROW END NOT NULL,PERIOD FOR SYSTEM_TIME([StartDate],[EndDate])
)WITH (SYSTEM_VERSIONING=ON(HISTORY_TABLE = [dbo].[AnimalsHistory]))

运行后,在SSMS中刷新Tables文件夹时,表名后会有(System-Versioned)字样。

如果要更改现有表,则需要为“开始日期”和“结束日期”定义默认值,如下所示:

ALTER TABLE [dbo].[Animals]
ADD StartDate datetime2(0) GENERATED ALWAYS AS ROW START CONSTRAINT P_StartDateConstraint DEFAULT'2000.01.01',
EndDate datetime2(0) GENERATED ALWAYS AS ROW END CONSTRAINT P_EndDateConstraint DEFAULTCONVERT(DATETIME2, '9999-12-31 23:59:59'),
PERIOD FOR SYSTEM_TIME(StartDate,EndDate);

在我的表中,我使用选项定义了历史表:WITH (SYSTEM_VERSIONING=ON(HISTORY_TABLE = [dbo].[AnimalsHistory]))

您可能注意到了,此表有聚集索引 ix_AnimalsHistory,并且两个表的字段都是一样的。

二、DML测试

1.   INSERT

Insert与普通插入没有区别

INSERT INTO dbo.[Animals]([Name],[Genus Species],[Number]) VALUES('African wild cat','Felis silvestris lybica',10);

快速检查两个表,可以看到Animals表有新行而历史表AnimalsHistory是空的。

可以插入一行并指定开始日期吗?——不可以

为开始日期添加默认值并使用约束插入行是否可以?——结果表明插入的是当前日期和时间,而不是我的默认值

如果插入时直接指定用默认值呢?——还是不行

对INSERT的结论非常简单,您无法指定开始日期。

下面我们多插入一些值,为后续测试作准备。

2.   update

我们执行两次下面的命令

update [Animals] set number=20 where name='ccc';

从执行计划中可以看出来实际上是更新了Animals表,插入了AnimalsHistory表(由于执行了两次,插入了两条记录)

3.   delete

当delete命令运行时,该值将存储在历史记录表中,并带有结束日期和时间。

delete from [Animals] where name='EEE';

三、 SELECT

1. 查询特定时间的值

使用 FOR SYSTEM_TIME AS OF 'datetime' 关键字。

select * from [Animals] FOR SYSTEM_TIME AS OF '2019-03-08 02:12:53' where name='ccc';

注意查的是原表而不是历史表,如果查历史表会遇到报错

select * from AnimalsHistory FOR SYSTEM_TIME AS OF '2019-03-08 02:12:53' where name='ccc';

2. 一次指定多个特定时间

FOR SYSTEM_TIME CONTAINED IN ('2019-03-08 02:12:53','2019-03-08 08:12:53')

select * from [Animals] FOR SYSTEM_TIME CONTAINED IN ('2019-03-08 02:12:53','2019-03-08 08:12:53');

3. 查指定时间范围内的值

  • FOR SYSTEM_TIME FROM '2019-03-08 02:12:53' TO '2019-03-08 08:12:53' 或者
  • FOR SYSTEM_TIME BETWEEN'2019-03-08 02:12:53' AND '2019-03-08 08:12:53'
select * from [Animals] FOR SYSTEM_TIME FROM '2019-03-08 02:12:53' TO '2019-03-08 08:12:53';

select * from [Animals] FOR SYSTEM_TIME BETWEEN'2019-03-08 02:12:53' AND '2019-03-08 08:12:53';

四、  DDL

1.   注意事项

1)在ALTER TABLE过程中,系统持有这两个表的架构锁。

2)对于系统版本控制的临时表,ALTER TABLE ALTER COLUMN 不支持 Online 操作。

3)不能使用直接 ALTER 进行以下架构更改。 对于这些类型的更改,请设置 SYSTEM_VERSIONING = OFF。

  • 添加计算列
  • 添加IDENTITY列
  • 当历史记录表设置为 DATA_COMPRESSION=PAGE 或 DATA_COMPRESSION=ROW(历史记录表默认值)时,添加SPARSE列或将现有列更改为SPARSE。
  • 添加COLUMN_SET
  • 添加ROWGUIDCOL列或将现有列更改为ROWGUIDCOL

2. Animals表添加一列

alter table [Animals] add test varchar(100);

可以看到历史表也已添加

看此时还能不能查询ddl前指定时间的数据

select * from [Animals] FOR SYSTEM_TIME AS OF '2019-03-08 02:12:53' where name='ccc';

发现还可以

3. 删掉Animals一列

alter table [Animals] drop column number;

看此时还能不能查询ddl前指定时间的数据

select * from [Animals] FOR SYSTEM_TIME AS OF '2019-03-08 02:12:53' where name='ccc';

还是可以,但是可以看到没有删除那列的数据了,因此时态表无法恢复误ddl操作数据。

4. truncate

truncate table [Animals];

发现不支持

查看官方文档,需要先暂停SYSTEM_VERSIONING

BEGIN TRAN
ALTER TABLE [Animals] SET (SYSTEM_VERSIONING = OFF);
truncate table [Animals]
ALTER TABLE [Animals] SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.AnimalsHistory));
COMMIT;

可以看到只清了时态表数据而没有清历史表数据

五、维护和元数据

1.   备份和还原

备份会包括原表和历史记录表(不可以选不备份),恢复过程与备份过程一样。

2.   Index Rebuild&Reorg

第一步是查看History表中的聚簇索引是否在碎片视图中。

为了确保能够在历史表上看到创建的所有索引,我创建一个新的非聚簇索引并重新运行查询。

下一步是验证是否可以在历史记录表中重建或重新组织索引,它运行得非常好。

3. 元数据SYS.TABLES

Sys.tables有3个新的临时表列:

  • temporal_type,标准表为0,历史表为1,系统版本表为2
  • temporal_type_desc,其中包含来自temporal_type列的3种类型的描述
  • history_table_id是系统版本表的id。

列出SQL Server数据库中的时态和历史表

select t.object_id,t.name,t.temporal_type,t.temporal_type_desc,h.object_id,h.name,h.temporal_type,h.temporal_type_desc
from sys.tables t
inner join sys.tables h on t.history_table_id = h.object_id;

4. sys.periods

5. OBJECTPROPERTYEX

OBJECTPROPERTYE中的新属性TableTemporalType精确地表示sys.tables中的temporal_type类型

六、  如何将普通表转换为时态表

先建一个普通表

create table systemParameters (ParameterId int identity(1,1) not null primary key nonclustered,Description varchar(100),Value nvarchar(400),
)

请注意,时态表(即系统版本源表)必须定义主键。否则,在将System_Versioning设置为ON或启用system_versioning时,SQL引擎会报错。

创建一个具有完全相同列的表。当然,还要有两个新的附加列

create table systemParametersHistory (ParameterId int not null,Description varchar(100),Value nvarchar(400),SysStartTime datetime2 Not Null,SysEndTime datetime2 Not Null
)

另请注意,主键不会在历史记录表中创建,历史表中的标识列也不允许这样做。

此时它们还是两个普通表

可以手动创建历史表或让SQL Server 自动创建,我们添加两个datetime2字段,用于源时态表上的有效性开始和结束日期标识。另外添加缺少的时间段列,它是时态表的System_Time。

ALTER TABLE systemParameters
Add SysStartTime datetime2 Generated Always as Row Start Not Null,SysEndTime datetime2 Generated Always as Row End Not Null,
Period for System_Time (SysStartTime, SysEndTime);

此时还是两个普通表

最后在时态表上启用系统版本控制,可以使用ALTER TABLE SET命令,并为system_versioning设置值ON。由于我们已手动创建了历史表,因此我们必须使用history_table参数传递历史表名。

alter table systemParameters set (system_versioning = on (history_table = dbo.systemParametersHistory));

可以看到两张表已经变成了时态表和历史表

如果没有选择手动创建历史表,则数据库中没有名为systemParametersHistory的表,自动创建临时的系统版本表的历史表。

如果懒于创建历史表或在ALTER TABLE语句中传递历史表名,则以下命令也将起作用。在执行结束时,在我们的数据库中会有一个以“MSSQL_TemporalHistoryFor_ {object_id of temporal table}”格式命名的新历史表。

alter table test02 set (system_versioning = on);

七、将时态表转回普通表

1. 将 SYSTEM_VERSIONING 设置为 OFF

如果希望对时态表执行特定的维护操作或者不再需要版本控制的表,请停止系统版本控制。

完成此操作后,将获得两个独立的表:

  • 具有时间段定义的当前表
  • 作为常规表的历史记录表

2.   重要提示

  • 设置SYSTEM_VERSIONING = OFF或删除SYSTEM_TIME时间段时不会出现数据丢失的情况。
  • 设置SYSTEM_VERSIONING = OFF且不删除SYSTEM_TIME时间段时,系统将继续更新每个插入和更新操作的时间段列。在当前表中执行的删除操作是永久性的。
  • 删除SYSTEM_TIME时间段以完全删除该时间段列。
  • 设置SYSTEM_VERSIONING = OFF时,具有足够权限的所有用户都能够修改历史记录表的架构和内容,甚至还可以永久删除该历史记录表。

3.   永久删除 SYSTEM_VERSIONING

此示例永久删除了 SYSTEM_VERSIONING 并完全删除了时间段列,删除时间段列是可选的操作。

ALTER TABLE dbo.Department SET (SYSTEM_VERSIONING = OFF);
/*Optionally, DROP PERIOD if you want to revert temporal table to a non-temporal*/
ALTER TABLE dbo.Department DROP PERIOD FOR SYSTEM_TIME;

4.   暂时删除 SYSTEM_VERSIONING

以下是需要将系统版本控制设置为 OFF的操作列表:

  • 从历史记录中删除不必要的数据(DELETE 或 TRUNCATE
  • 从没有版本控制的当前表中删除数据(DELETE、 TRUNCATE
  • 从当前表对 SWITCH OUT 进行分区
  • 将 SWITCH IN 分区到历史记录表

最后,时态表的缺点。它加大了维护负担,历史表保存每一个dml操作也加大了存储负载,另外它无法恢复误ddl操作。

参考

https://blog.dbi-services.com/sql-server-2016-the-time-travel-with-temporal-table-part-i/

http://www.kodyaz.com/sql-server-2016/create-sql-server-2016-temporal-table.aspx

https://docs.microsoft.com/zh-cn/sql/relational-databases/tables/creating-a-system-versioned-temporal-table?view=sql-server-2017

SQL Server的时态和历史表相关推荐

  1. SQL Server - 使用 Merge 语句实现表数据之间的对比同步

    SQL Server - 使用 Merge 语句实现表数据之间的对比同步 原文:SQL Server - 使用 Merge 语句实现表数据之间的对比同步 表数据之间的同步有很多种实现方式,比如删除然后 ...

  2. Oracle和sql server中复制表结构和表数据的sql语句

    在Oracle和sql server中,如何从一个已知的旧表,来复制新生成一个新的表,如果要复制旧表结构和表数据,对应的sql语句该如何写呢?刚好阿堂这两天用到了,就顺便把它收集汇总一下,供朋友们参考 ...

  3. Mysql与Sql Server查询数据库中表以及表字段

    1.查询数据库表信息 mysql查询数据库中所有表信息 SELECTtable_name AS '表名',table_comment AS '说明',create_time AS '创建时间',upd ...

  4. Java案例:连接SQL Server数据库,显示学生表记录

    Java案例:连接SQL Server数据库,显示学生表记录 演示利用JDBC连接SQL Server数据库,在Java GUI窗口里显示表记录. 一.运行效果 二.实现步骤 1.项目结构图

  5. cte公用表表达式_CTE SQL删除; 在SQL Server中删除具有公用表表达式的数据时的注意事项

    cte公用表表达式 In this article, the latest in our series on Common table expressions, we'll review CTE SQ ...

  6. Sql Server获取数据库名,表信息,字段信息,主键信息等

    --Sql Server获取数据库名,表信息,字段信息,主键信息等--获取所有数据库名: SELECT name FROM master..sysdatabases WHERE name NOT IN ...

  7. SQL Server使用快捷键查看指定表的信息(字段、备注、索引、约束信息等)

    在我们工作,学习,开发中,为了快速了解业务,编写业务,需要了解相关的表的信息,下面这个SQL就很有必要了.这个SQL能让我们使用快捷键就能查看指定表的信息(字段.备注.索引.约束信息等),能让我们迅速 ...

  8. SQL Server管理相关的注册表技巧

    SQL Server管理相关的注册表技巧 2010-10-20 17:14 佚名 SQL Server的管理和Windows的管理是息息相关的,通过Windows的注册表可以让SQL Server管理 ...

  9. sql server 触发器实时同步数据库表数据

    sql server 触发器实时同步数据库表数据 创建两个相同结构的数据库表 CREATE TABLE [dbo].[Table_1]([id] [varchar](50) NOT NULL,[nam ...

最新文章

  1. Windows系统下多版本GCC的安装: MinGW Cygwin Msys2 和 VS: MSVC
  2. AIX下镜像制作与取消,更换硬盘
  3. jsp静态导入和动态导入
  4. 老鼠实验中老鼠的数量变化曲线
  5. 通过init进程看如何启动第一个应用程序
  6. [iPhone高级] 基于XMPP的IOS聊天客户端程序(IOS端二)
  7. Entity Framework 实体框架的形成之旅--为基础类库接口增加单元测试,对基类接口进行正确性校验(10)...
  8. python 使用c模块_您可能没有使用(但应该使用)的很棒的Python模块
  9. 如何使用阿里云ARMS轻松重现用户浏览器问题
  10. NG Updata(升级)
  11. 你写过哪些原创的风骨傲气,热血沸腾的句子?
  12. vue-router 懒加载优化
  13. Work Queues(点对多)
  14. 6.面向对象的三大特征
  15. 金蝶K3运行时错误-2147467238(8000401a)Automation错误
  16. mysql环境变量配置还是不行_为什么要配置mysql环境变量
  17. 民航订票管理系统设计
  18. PHP资源汇总,内容包括:库、框架、模板等
  19. 大物狭义相对论中的四维时空与闵氏时空图(上)
  20. postgres用户管理及权限控制--赋予某账号只读权限

热门文章

  1. Android 开源1:获取并解析网页信息(Jsoup)
  2. 20000字干货,数据分析 Pandas 75个高频操作都在这儿了!
  3. 目标与哲学---论《道德情操论》与…
  4. 【英语】大学英语CET考试,口语部分1(考试介绍与备考,讲义笔记)
  5. 谈 Scratch 版“植物大战僵尸”
  6. opencv图像形态学运算
  7. Least Crucial Node UVALive - 7456
  8. 10 Java基础笔记-封装
  9. AI反欺诈:千亿的蓝海,烫手的山芋|甲子光年
  10. css案例17——圆角头像