本文主要介绍源表为单表时,数据增量抽取的情况。当源表为多表时,后面的文章会继续介绍。
一、抽取情况说明
将源数据库S中的A表(将此表称为源表),通过ETL工具抽取至目标数据库T的A表(将此表称为目标表)。假设源表A的表结构与目标表A的表结构完全一致。表结构如下图所示:
采用用增量时间戳的方式进行增量抽取,需要目标数据库T中建立一张抽取日志表LOG,来记录每次抽取的情况。
表结构如下图所示:
其中,KSSJ、JSSJ字段为保存的值为源表A中每次抽取时,根据ZHXGSJ字段进行抽取数据过滤的字段值。
二、抽取过程
1、结合源表的数据结构并确认源表的增量时间戳字段(在这里此字段名为ZHXGSJ,即最后修改时间)。注意,时间戳字段不能为空。若为空,则不能采用此方法进行数据抽取。
2、单次抽取流程图,如下图所示:
1)、开始。
2)、判断源表A中的数据记录数是否为0。即在源数据库S下执行下面语句:select count(1) from A。
3)、若记录数为0,则说明源表为空,不需要进行数据抽取,结束。
4)、若记录数不为0,查询源表A的最后修改最大时间,即max(ZHXGSJ),将此时间记作JSSJ。即在源数据库S下执行下面语句:select max(ZHXGSJ) from A。将此语句执行结果保存至变量JSSJ。
5)、判断JSSJ是否为空。若JSSJ为空,则说明ZHXGSJ字段值都为空,则不能采用增量时间戳的方法进行增量抽取,结束。否则,执行下一步。
6)、获取抽取日志LOG表中距离当前时间最近的一条抽取记录,即max(JSSJ) ,将此时间记作KSSJ。即在目标数据库T下执行下面语句:select max(JSSJ) from LOG。将此语句执行结果保存至变量KSSJ。
7)、判断KSSJ是否为空。
8)、若KSSJ为空,则说明此过程为第一次抽取;因此,查询源表A的ZHXGSJ满足小于等于JSSJ的所有数据记录。即在源数据库S下执行下面语句:select * from A where ZHXGSJ <=:JSSJ。
9)、若KSSJ不为空,则说明此过程不是第一次抽取;因此,查询源表A的ZHXGSJ满足大于KSSJ且小于等于JSSJ的所有数据记录。即在源数据库S下执行下面语句:select * from A where ZHXGSJ > :KSSJ AND ZHXGSJ <=:JSSJ。
10)、通过ETL等工具,将源表A中符合条件的数据记录抽取至目标表A。即根据第8步或者第9步中获取的源表A中符合条件的所有数据记录,通过ETL等工具抽取至目标表A。
10)、根据KSSJ,JSSJ,插入一条记录到抽取日志表。即在目标数据库T下执行下面语句:insert into LOG(ID,KSSJ,JSSJ) values (:ID,:KSSJ, :JSSJ)。
11)、结束。
注意:
1)、上述第7步可以省略,同时第8步、第9步、可以合并为一个步骤,即合并为第9步(当然需要对第9步内容进行调整下)。因为,当KSSJ为空时,可将KSSJ的值设置为一个默认值,即1970年1月1日。在实际操作下经常这么做。
2)、本流程图,只是用来说明增量抽取的原理的,实际情况,使用ETL等相关工具进行抽取流程配置的时候会有所不同。
3、设置定时任务,根据实际情况,定时执行单次抽取过程。
三、需要注意的地方
理论上,根据此方法进行抽取,源表A和目标表A,无论是数量还是每条数据记录上都理应是完全一致的。要注意的是,这仅仅是理论上,实际情况中,会有多种原因造成源表A和目标表A,存在不一致的情况。在这不做讨论,后面的有文章会专门讨论,出现这种情况的原因。
四、其他
前段时间,面试了一个数据库工程师,提了一些关于数据增量抽取的问题。他说,他们是用sysdate-1(对于oracle来说)来确认增量数据的。也就是上述单次抽取过程中第9步,是根据sysdate-1过滤增量数据的。按照本例的话,就是源数据库S下执行下面语句:select * from A where ZHXGSJ >=sysdate-1。我问:如果你的定时任务停了一天,那怎么办?他回答改为sysdate-2。我又问:你不觉得你这种做法会很可能会造成数据抽取遗漏的问题吗?他回答,不会啊,我一直都是这么做的,没什么问题。
对于这位工程师的做法,我不能说是错误的,只能说是不够严谨、不够专业的。当然不止一个工程师这样回答,当然,在实际工作中,我也见过有不少工程师,使用sysdate-1这种方法进行增量抽取的。其中,不乏专门做数据同步的公司。像我这种“半个数据库工程师”都知道采用sysdate-1这种做法是有问题的,可有些拥有四五年工作经验的数据库工程师,竟然不清楚。