在数据仓库项目之中,有表
工单表(资产代码,系统代码,位置代码,功能位置代码),
物体关系表(资产代码,系统代码,位置代码,功能位置代码),
其中,物体关系表是通过直接导入的方式从现有的数据库中导入的,该表维护着所有资产、位置等等一切物体之间的关系,也就是说其中的大部分字段都是有值的,而工单表是数据仓库项目的事实表。需要从源库的数据表经过ETL将数据转换得到。现在我的工作是对工单表进行ETL。
在ETL的时候,过来一条数据,代表一个工单,该工单有可能记录了对资产、系统、位置、功能位置其中一种数据,当过来的数据是资产的时候,可以根据资产代码去查出资产的系统、位置等代码,当过来的数据是功能位置时,可以根据功能位置去查出地理位置。
最开始的时候,我是写了一个ORACLE的函数,每当过来的工单是资产类型的,就调用函数到源数据库中去查该资产的其他代码,对功能位置也是如此。开始在测试库中进行ETL时,数据只有几千条,ETL的过程比较顺利。之后,数据库迁移到接近正式的运营数据库,这时候工单表和关系表的数据量均达到了百万的级别。在每条数据ETL的时候都调用函数去查源表,也就是说百万条数据,每条数据经过都要去遍历另外一个百万条数据的表才能找到相应的关系代码,而且关系代码是树形结构,还不能直接得到。
开始的时候为了实现功能,用函数去查找代码,为了实现树形的查询使用了 START WITH (CONDITION)CONNECT BY PRIOR (等式) 语句,该语句可以将以START WITH后面的语句为子节点的所有祖先节点查询出来。那么,基本的功能就实现了。
迁移数据库之后,因为数据量的剧增,还发生了Informatica JION时缓存不足的事情,后来才知道 当你对Informatica的缓存不做设置的时候,其默认大小是差不多1M左右,反正很小,做两个百万级别的表的JION就会缓存不足。但是可以手动设置join的缓存。缓存的事情解决之后,对工单表进行ETL时,一个ETL跑了整整20个小时才跑出差不多二十分之一的数据。所以,我发现不能这样做,于是想到在ETL做完,将数据先抽到目标表,之后再写存储过程去对相应的代码进行更新。因为之前ETL的时候,源数据库、目标库、ETL服务器分别在不同机器和数据库中,这样每次去调用函数到源库查代码,那么中间对任务的管理和IO肯定是非常巨大的。所以决定用存储过程统一更新。
但是开始在做存储过程的时候,关系数据在源库,不可能每次在目标库查询关系都去调用源库的表,所以以中间表的方式将源库内的关系以增量更新的方式导入到目标库。之后再建一张中间表,在该表中已经通过调用函数将每个物体的关系 也就是(资产代码、位置代码、系统代码、功能位置代码)先查出来,而不是每次要查物体关系的时候都要到关系表去查,直接通过中间表与事实表的关联就可以一次把关系全部查询出来。
为了对每条数据进行更新,使用游标(CURSOR)的方式对每条数据进行遍历,因为之前的SQL语句基本上都是进行批量的更新。但是,每一条数据虽然不用去查其关系,但还是要几乎遍历一张八十万左右的中间表才能找到该数据的各种关系代码。于是,对常用的查询条件中的字段,比如资产代码、物体类型等字段建立索引(INDEX),建立索引之后查询的速度确实会有一些提升。后来对一个八十万左右数据的表进行更新和ETL的时间基本上在三分钟到四分钟。
上面所做的工作是对资产这张表。后来要对工单这张表进行ETL,工单这张表与资产的区别就是,资产表是以资产代码为主键,而在工单表中资产只是一个外键。其中详细的内容就不再赘述,简而言之就是资产的这个存储过程的形式在工单这张表上不能适用,因为我用这个存储过程跑工单表的ETL时,也需要好几个小时。
为了将ETL的时间降低到可接受的范围之内,做了诸多尝试,建立索引、分区,都无法成功,还尝试了Merge into TABLE USING TABLE ON WHEN MATCHED THEN,因为之前说过我建了一个中间表将各种物体的关系预处理好了。这里也要感谢很多CSDN的很多前辈在论坛上对我提出的问题进行了解答,虽然对他们来说可能只是敲几下键盘,却给了我很多帮助。尝试了MERGE之后发现效果并不理想。因为Merge据说在做批量的更新和插入上很有效率,但是我这些表都是做增量的更新,除了第一次,之后的数据量更新不会很大,最终还是采用了坛友的建议,直接使用SQL语句进行更新。
UPDATE TABLE A
SET TABLE A.CODE=(SELECT B.CODE FROM TABLE B WHERE A.CODE=B.CODE)
开始我对UPdate的理解都是对一个集合进行更新,开始想到这个语句的时候我想,会不会把表A中凡是满足A.CODE=B.CODE的code都会更新,因为A和B中相同的CODE可能会有很多个,而我要的并不是这样。后来才知道,其实UPDATE这个词,在进行更新时,会把A的数据一条一条地带入到后面的条件进行测试,如果测试条件为真就会对这条数据进行更新。
看个例子 比如
A中有两条数据 (1,2)(2,3)B中有一些数据(1,4)(2,5)
那么我写一个
UPDATE TABLE A
SET A.第二个字段=(SELECT B.第二个字段 FROM TABLE B WHERE B.第一个字段=A.第一个字段)
那么执行完这条SQL之后,A中的数据会变成(1,4)(2,5)。
其实就是用两个表的第一个字段相等这个条件,用B的数据对A的第二个字段进行更新。
通过这种办法,工单表的ETL基本上能够控制在四分钟左右,基本上可以接受了。
在这之中,还有一个需要注意的地方就是,在写一个复杂度或者数据量比较大的存储过程的时候,在执行完一个部分的时候一定要COMMIT,因为提交可以让事务的粒度变小,若不提交则整个存储过程会当成一个事务。
整个做完之后真的有一种稼轩公“蓦然回首那人却在灯火阑珊处”的感觉啊,绕了一大圈,又是函数、游标、存储过程,最终还是用标准的SQL语句解决了。 也许这对很多人来说非常简单,但对我来说是很宝贵的经验。感谢那些帮助我的人!