设为首页 收藏本站
查看: 1769|回复: 1

[经验分享] SQL Server ->> 深入探讨SQL Server 2016新特性之 --- Temporal Table(历史表)

[复制链接]

尚未签到

发表于 2018-6-28 09:36:29 | 显示全部楼层 |阅读模式
  作为SQL Server 2016(CTP3.x)的另一个新特性,Temporal Table(历史表)记录了表历史上任何时间点所有的数据改动。Temporal Table其实早在ANSI SQL 2011就提出了,而SAP HANA, DB2和Oracle早已在它们的产品中加入/实现了这一特性。所以说微软其实是落后了几个竞争对手。既然在CTP3.0中加入了,相信RTM也肯定有这个特性。
  Temporal Table(历史表)有何作用?
  1)审计数据改动,为报表和数据分析提供支持,洞察记录的变化趋势
  2)实现了ETL中的Slowly Changing Dimension的类型2(保留所有数据的旧版本)
  3)一旦发生误操作的情况下可以及时进行数据恢复
  Temporal Table(历史表)和CDC的区别
  以前微软为ETL提供了CDC功能来记录数据改动。Temporal Table同样是用于记录数据改动,但是它俩不一样。第一点,Temporal Table不像CDC是基于事务日志,它是作为事务的一部分被提交的。第二点,CDC是每次对新的表记录最新版本拷贝一份到另外一张表,而Temporal Table是把旧版本的记录转移到另外一张表,只有把Temporal Table和当前表的记录合并才可以构成表的整个历史版本(记录没有被删除的情况下)。
  Temporal Table(历史表)的条件?
  1)必须有主键;2)两个记录有效时间范围字段必须为not null;3)历史表必须是和主表在结构上一模一样,包括字段名字和数据类型;
  Temporal Table(历史表)如何实现?
  Temporal Table其实对一对数据库表进行数据版本化(System-versioning)。一张是主表,一张是主表的历史记录表。Temporal Table的条件之一是添加两个类型为datetime2的字段来标示记录的有效时间范围 -- SysStartTime和SysEndTime。这两个字段是有系统自动更新的,可以选择在建表的时候对字段加入HIDDEN提示把字段隐藏,这样就避免在SELECT * FROM或者INSERT INTO的时候出现两个字段在列表里面。当插入(insert)发生时,事务开始的时间作为主表的SysStartTime,SysEndTime则被更新为9999-12-31,历史表不会有任何变化。当更新(update)发生时,历史记录表中的SysEndTime被更新为事务开始的时间,主表的SysStartTime则被更新为事务开始的时间,SysEndTime则被更新为9999-12-31。当删除(delete)发生时,历史记录表中的SysEndTime被更新为事务开始的时间。
  查询Temporal Table(历史表)的记录
  SQL Server对T-SQL提供了几个新的子句用于查询Temporal Table中的记录,即在正常的T-SQL查询语句后面添加新的子句:
FOR SYSTEM_TIMEALL, AS OF, BETWEEN...AND, FROM...TO, CONTAINED IN  这里如果SysStartTime和SysEndTime相等时不会返回记录的。
  AS OF <date_time>                     等于SysStartTime<= date_time AND SysEndTime> date_time
  FROM <start_date_time> TO <end_date_time>      等于SysStartTime< end_date_time ANDSysEndTime> start_date_time
  BETWEEN <start_date_time> AND <end_date_time>    等于SysStartTime<= end_date_time ANDSysEndTime> start_date_time
  CONTAINED IN(<start_date_time> ,<end_date_time>)     等于SysStartTime>= start_date_time ANDSysEndTime<= end_date_time
  ALL                             等于没有任何筛选条件
  下图是来自MSDN的一张图,我觉得用于描述Temporal Table(历史表)的工作流程非常确切
DSC0000.jpg

  Temporal Table(历史表)的最佳实践?
  1)如果是做数据分析,比如统计一个平均值或者总数这样的分析,在History表的主键上建聚集列存储索引(clustered columnstore index);
  2)如果是做审计,对数据行有效时间范围字段建聚集索引,然后对主键也建立索引;
  创建Temporal Table(历史表)
  History table是不会出现在SQL Server Management Studio的Object Explorer窗口的,但是你可以通过sys.tables找出来。
  Temporal Table(历史表)可以有三种方法创建:1)你完全不关心名字,让SQL Server帮你创建包括帮你自动生成表名;2)你指定表名然后让SQL Server根据表名为你生成表结构;3)你事先创建好表;
  由SQL Server自动创建History表
DSC0001.gif

CREATE TABLE dbo.TemporalTableTEST1  
(
  
     ID INT PRIMARY KEY CLUSTERED
  
   , SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL
  
   , SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL
  
   , PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)
  
)WITH ( SYSTEM_VERSIONING = ON);

  自己指定表名字和自己事先创建好表都是一样的语法。如果表已经存在,表的结构会被检查。检出出问题命令失败。

CREATE TABLE dbo.TemporalTableTEST2  
(
  
     ID INT PRIMARY KEY CLUSTERED
  
   , SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL
  
   , SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL
  
   , PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)
  
)WITH ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.TemporalTableTEST2_History));

  因为事先创建好的表可能已经存在数据行,建议添加好DATA_CONSISTENCY_CHECK来同时检查表中的数据行。建议在事先创建好表的情况下添加DATA_CONSISTENCY_CHECK选项

CREATE TABLE dbo.TemporalTableTEST3  
(
  
     ID INT PRIMARY KEY CLUSTERED
  
   , SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL
  
   , SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL
  
   , PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)
  
)WITH ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.TemporalTableTEST3_History, DATA_CONSISTENCY_CHECK = ON));

  如果是对一张现有表进行转换,要分两种情况:一种是表是空表,一种是表里面已经存在数据行。下面是对一张空表转换成Temporal Table的例子

--DROP TABLE dbo.TemporalTableTEST5CREATE TABLE dbo.TemporalTableTEST5  
(
  
     ID INT PRIMARY KEY CLUSTERED)GOSELECT * FROM dbo.TemporalTableTEST5GOALTER TABLE dbo.TemporalTableTEST5ADD SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START        CONSTRAINT DF_TemporalTableTEST5_SysStart DEFAULT SYSUTCDATETIME() ,
  
    SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END
  
        CONSTRAINT DF_TemporalTableTEST5_SysEnd DEFAULT CONVERT(datetime2 (0), '9999-12-31 23:59:59.9999999'),
  
    PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)GOALTER TABLE dbo.TemporalTableTEST5SET ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.TemporalTableTEST5_History, DATA_CONSISTENCY_CHECK = ON));GO

  如果表中有数据,需要分开来完成这个转换过程。

--DROP TABLE dbo.TemporalTableTEST4CREATE TABLE dbo.TemporalTableTEST4  
(
  
     ID INT PRIMARY KEY CLUSTERED)GOINSERT INTO dbo.TemporalTableTEST4(ID)VALUES(1),(2),(3)GO--INSERT INTO dbo.TemporalTableTEST4(ID)--VALUES(4),(5),(6)--GOALTER TABLE dbo.TemporalTableTEST4ADD SysStartTime DATETIME2 NOT NULL CONSTRAINT DF_TemporalTableTEST4_SysStart DEFAULT SYSUTCDATETIME() ,
  
    SysEndTime DATETIME2 NOT NULL CONSTRAINT DF_TemporalTableTEST4_SysEnd DEFAULT CONVERT(DATETIME2, '9999-12-31 23:59:59.9999999')GOSELECT * FROM dbo.TemporalTableTEST4GO--UPDATE dbo.TemporalTableTEST4 SET SysEndTime =  '9999-12-31 23:59:59.9999999'--GO--ALTER TABLE dbo.TemporalTableTEST4--ALTER COLUMN SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START--ALTER TABLE dbo.TemporalTableTEST4--ALTER COLUMN SysEndTime DATETIME2 GENERATED ALWAYS AS ROW ENDALTER TABLE dbo.TemporalTableTEST4ADD PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime);GOALTER TABLE dbo.TemporalTableTEST4SET ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.TemporalTableTEST4_History, DATA_CONSISTENCY_CHECK = ON));GO

  如果你直接用第一种办法就会收到一个错误提示
Msg 13575, Level 16, State 0, Line 53ADD PERIOD FOR SYSTEM_TIME failed because table 'JerryDB.dbo.TemporalTableTEST4' contains records where end of period is not equal to MAX datetime.  为了证明SQL Server只要求主表和历史表的字段结构和约束一致,不要求分区和压缩选项一致,这里做一个实验

CREATE PARTITION FUNCTION myPF (int)AS RANGE LEFT FOR VALUES (1, 100, 1000);GOCREATE PARTITION SCHEME myPS1AS PARTITION myPFTO ( [primary], [primary], [primary], [primary] );--DROP TABLE dbo.TemporalTableTEST7_HistoryCREATE TABLE dbo.TemporalTableTEST7_History  
(
  
     ID INT NOT NULL
  
   , SysStartTime DATETIME2 NOT NULL
  
   , SysEndTime DATETIME2 NOT NULL)
  
ON myPS1(ID)WITH (DATA_COMPRESSION = PAGE )CREATE TABLE dbo.TemporalTableTEST7
  
(
  
     ID INT PRIMARY KEY CLUSTERED
  
   , SysStartTime DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL
  
   , SysEndTime DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL
  
   , PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)
  
)WITH ( SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.TemporalTableTEST7_History));

  上面是可行的。
  总结一下:
  总之记住几个点
  1)PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)和SYSTEM_VERSIONING都是Temporal Table的特性,但是它俩在某些方面是不互相依赖。SYSTEM_VERSIONING是起到启动历史表的作用,PERIOD FOR SYSTEM_TIME是为标示表记录有效时间范围而存在,属于表结构属性的范畴。
  2)PERIOD FOR SYSTEM_TIME隐式的将连个SYSTEM TIME的字段转换成AS ROW STARTS和AS ROW ENDS
  3)SYSTEM_VERSIONING是不会阻止你去更新主键的,所以一旦你更新了主键,将会导致主表和历史表的记录错乱;
  4)虽然PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)和SYSTEM_VERSIONING在某些方面不互相依赖,但是要更新SYSTEMTIME字段,需要SET SYSTEM_VERSIONING = OFF
  对PARTITION SWITCH的支持
  条件是stage表需要有SYSTEMTIME PERIOD,但是不要求表必须是SYSTEM_VERSIONING。这里对主表和历史表的限制是
  主表:在SYSTEM_VERSIONING=ON的情况下SWITCH OUT是不允许的,我们都知道PARTITION SWITCH仅是元数据(metadata)的变动,这样History表是捕捉不到分区内数据的,所以行不通;SWITCH IN在SYSTEM_VERSIONING=OFF的情况下是可以进行的,毕竟这样也不会对History表有什么影响,因为SWITCH IN相当于INSERT行为,INSERT对于History表没有影响。
  历史表:SWITCH OUT可以在SYSTEM_VERSIONING=ON的情况下进行;SWITCH IN在SYSTEM_VERSIONING=ON的情况下则不行,因为这本身就违反了History表的数据验证流程;
  注:
  这里所说的Data Consistency检查只是检查是否SysStartTime<=SysEndTime
  对Temporal Table的主表的结构改动
  这一步SQL Server倒是做得挺好的,就是不需要你改完主表还要去改历史表。比如你添加一个字段和一个默认约束到主表,历史表也自动应用到同样的改动。

  Name ()    DK_TemporalTableTEST4_Name  select * from dbo.TemporalTableTEST4
  select * from dbo.TemporalTableTEST4_History

  结果
DSC0002.jpg

  某些情况下我们可以不停用SYSTEM_VERSIONING的情况下照样完成了对主表的结构改动,比如添加一个正常的字段(非compted等),但是如果添加诸如identity或者computed字段则需要停用system_versioning。否则
Msg 13724, Level 16, State 1, Line 135System-versioned table schema modification failed because adding computed column while system-versioning is ON is not supported.  但是有一点是例外,就是如果你要删除主表的一个字段,除了要删除主表字段上创建的约束外还要删除历史表上对应字段的约束,否则
Msg 5074, Level 16, State 1, Line 142The object 'DF__TemporalTa__dttm__02FC7413' is dependent on column 'dttm'.  
Msg 4922, Level 16, State 9, Line 142ALTER TABLE DROP COLUMN dttm failed because one or more objects access this column.
  查询数据
  上面讲了FOR SYSTEM_TIME的五个子句 AS OF | FROM...TO | BETWEEN...AND | CONTAINED IN (<START>,<END>) | ALL
  理解这几个其实很容易,完全无需去记住他们所应用的WHERE表达式。
  ALL = 全部嘛,这个不用讲
  AS OF = AS OF的英文意思是自...开始,那就是某个时间点有效(包括这个时间点)的行
  FROM... TO = 时间区间内有效的行,但是不包含开闭的时间点,即不包含上限
  BETWEEN...AND = 时间区间内有效的行,包含开的时间点,即包含下限
  CONTAINED IN (<START>,<END>) = CONTAINED的意思是包含,也就是说记录有效区间处在我们指定的时间区间这个容器内
  拿上面的TemporalTableTEST5来demo。先准备好数据。

ALTER TABLE dbo.TemporalTableTEST5ADD float_col FLOATGOINSERT INTO dbo.TemporalTableTEST5(ID, float_col)VALUES(1,100),(2,200),(3,300)GOUPDATE dbo.TemporalTableTEST5 SET float_col = float_col + 50WHERE ID = 1UPDATE dbo.TemporalTableTEST5 SET float_col = float_col + 50WHERE ID = 1UPDATE dbo.TemporalTableTEST5 SET float_col = float_col + 50WHERE ID = 1

  查询全部的历史数据
SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5  
FOR SYSTEM_TIME ALL

ID    float_col    SysStartTime    SysEndTime1    250    2016-02-21 09:27:52.0379197    9999-12-31 23:59:59.99999992    200    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999993    300    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999991    100    2016-02-21 09:27:38.6363057    2016-02-21 09:27:40.51624461    150    2016-02-21 09:27:40.5162446    2016-02-21 09:27:46.43125591    200    2016-02-21 09:27:46.4312559    2016-02-21 09:27:52.0379197

  AS OF
SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5  
FOR SYSTEM_TIME AS OF '2016-02-21 09:27:38.6363057';
ID    float_col    SysStartTime    SysEndTime2    200    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999993    300    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999991    100    2016-02-21 09:27:38.6363057    2016-02-21 09:27:40.5162446  FROM...TO

SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5  
FOR SYSTEM_TIME FROM '2016-02-21' TO '2016-02-21 09:27:38.6363057'SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5
  
FOR SYSTEM_TIME FROM '2016-02-21 09:27:40.5162446' TO '2016-02-22'

  第一个没有记录返回
  第二个返回了

ID    float_col    SysStartTime    SysEndTime1    250    2016-02-21 09:27:52.0379197    9999-12-31 23:59:59.99999992    200    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999993    300    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999991    150    2016-02-21 09:27:40.5162446    2016-02-21 09:27:46.43125591    200    2016-02-21 09:27:46.4312559    2016-02-21 09:27:52.0379197

  把FROM...TO的例子替换成BETWEEN...AND

SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5  
FOR SYSTEM_TIME BETWEEN '2016-02-21' AND '2016-02-21 09:27:38.6363057'SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5
  
FOR SYSTEM_TIME BETWEEN '2016-02-21 09:27:40.5162446' AND '2016-02-22'

  第一个返回了
ID    float_col    SysStartTime    SysEndTime2    200    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999993    300    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999991    100    2016-02-21 09:27:38.6363057    2016-02-21 09:27:40.5162446  第二个返回了

ID    float_col    SysStartTime    SysEndTime1    250    2016-02-21 09:27:52.0379197    9999-12-31 23:59:59.99999992    200    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999993    300    2016-02-21 09:27:38.6363057    9999-12-31 23:59:59.99999991    150    2016-02-21 09:27:40.5162446    2016-02-21 09:27:46.43125591    200    2016-02-21 09:27:46.4312559    2016-02-21 09:27:52.0379197

  还是上面的例子改写,变成CONTAINED IN

SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5  
FOR SYSTEM_TIME CONTAINED IN ('2016-02-21', '2016-02-21 09:27:38.6363057')SELECT ID, float_col, [SysStartTime],[SysEndTime] FROM [dbo].TemporalTableTEST5
  
FOR SYSTEM_TIME CONTAINED IN ('2016-02-21 09:27:40.5162446', '2016-02-22')

  结果就是第一个没有返回任何结果
  第二个返回了
ID    float_col    SysStartTime    SysEndTime1    150    2016-02-21 09:27:40.5162446    2016-02-21 09:27:46.43125591    200    2016-02-21 09:27:46.4312559    2016-02-21 09:27:52.0379197  这个语法同样适用于UPDATE
  讲了这么多,Temparal Table还是有需要方面可讲的,比如它对In-memory OLTP Optimized Table的支持啦,比如安全的考虑啦。真要将估计很多。姑且到这。今后有机会再深究下。最后,在这里思考下到底这个东西在现实生产环境中可以怎么好好利用或者结合其他的特性一起发挥它的最大价值呢?
  1)首先我觉得基于上面讲到的可以作为数据误操作的数据复原。Temparal Table 结合SQL Server Audit。Temparal Table实现记录历史记录改动,而SQL Server Audit提供了对用户行为的审计。两者通过时间来关联。这样我们就是当初这条旧的历史版本记录是被谁改动或者删除的。然后对SQL Server Audit加载的目标表创建聚集索引到时间行以及以时间字段创建分区表。再创建非聚集索引到Object字段,再结合PAGE COMPRESSION压缩SQL Server Audit加载的目标表的数据行。
  2)报表分析这个案例我觉得视情况而定,要看到底是为了查看某条或者若干记录过去的变化趋势,还是查看数据分组后的平均变化情况或者是一些总量之类的东西。前者我觉得对Temparal Table的历史表应用聚集索引配合PAGE COMPRESSION,后者对Temparal Table的历史表创建Clustered Columnstore Index,加上以分区表技术(时间字段选择end time)。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-531571-1-1.html 上篇帖子: Windows server 2008R2 DHCP服务器 下篇帖子: 唠唠 RDS 那些事 —— RDS on Windows Server 2016 第一篇
累计签到:101 天
连续签到:44 天
发表于 2019-1-11 10:37:59 | 显示全部楼层
了解一下

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表