3.1 历史拉链表
数据仓库定义:是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。
历史拉链表:一种数据模型,主要是针对数据仓库设计中表存储数据的方式而定义的。它记录了一个事物从开始到当前状态的所有变化的信息。避免了按每一天存储所有记录造成的海量存储问题,同事也是处理缓慢变化数据的一种常见方式。
3.1.1 应用场景描述
企业有5000万会员信息,每天有20万会员资料变更,每天要保存一份数据快照以记录历史变化。那么一年数据条数为2x365x5000w是365亿条。如果用历史拉链表那么,每天只需向历史表中添加新增和变化的数据量,每天不过20万条。
3.1.2 原理及步骤
拉链表中的每一条数据都有一个生效日期(dw_beg_date)和失效日期(dw_end_date)。
member_fatdt0:表示member的事实表,其中P30001231保存的是最新数据,每个分区保留的都是历史已失效的数据。
 member_delta:当天的数据库变更数据,action字段表示该数据为新增(I)、更新(U)、删除(D)。
3.3.3 表结构
- 拉链表(member_facdt0)结构
 
create table public.member_fatdt0
(
	Member_id	varchar(64)   --会员ID
	,phoneno 	varchar(20)	  --电话号码
	,dw_beg_date date	      --生效日期
	,dw_end_date date         --失效日期
	,dtype       char(1)      --类型(历史数据、当前数据)
	,dw_status   char(1)      --数据操作类型(I,D,U)
	,dw_ins_date date         --数据仓库插入日期
) with(appendonly=true,compresslevel=5)
distributed by(member_id)
partition by range(dw_end_date)
(
partition p20111201 start (date '2011-12-01') inclusive,
partition p20111202 start (date '2011-12-02') inclusive,
partition p20111203 start (date '2011-12-03') inclusive,
partition p20111204 start (date '2011-12-04') inclusive,
partition p20111205 start (date '2011-12-05') inclusive,
partition p20111206 start (date '2011-12-06') inclusive,
partition p20111207 start (date '2011-12-07') inclusive,
partition p20111231 start (date '2011-12-31') inclusive
end (date '3001-01-01') exclusive
);
 
- 增量表(member_delta)结构
 
Create table public.member_delta
(
	Member_id		varchar(64)			--会员id
	,phoneno		varchar(20)			--电话号码
	,action			char(1)				--类型(新增,删除,更新)
	,dw_ins_date 	date				--数据仓库插入日期
) with(appendonly=true,compresslevel=5)
distributed by(member_id);
 
- 临时表0(member_tmp0)结构
 
Create table public.member_tmp0
(
	Member_id			varchar(64)			--会员ID
	,phoneno			varchar(20)			--电话号码
	,dw_beg_date		date				--生效日期
	,dw_end_date		date				--失效日期
	,dtype				char(1)				--类型(历史数据、当前数据)
	,dw_status			char(1)				--数据操作类型(I,D,U)
	,dw_ins_date		date				--数据仓库插入日期
) with(appendonly=true,compresslevel=5)
distributed by(member_id)
partition by list(dtype)
(
	partition phis values ('H'),
	partition pcur values ('C'),
	default partition other
);
 
- 临时表1(member_tmp1)结构
 
Create table public.member_tmp1
(
	Member_id			varchar(64)			--会员ID
	,phoneno			varchar(20)			--电话号码
	,dw_beg_date		date				--生效日期
	,dw_end_date		date				--失效日期
	,dtype				char(1)				--类型(历史数据、当前数据)
	,dw_status			char(1)				--数据操作类型(I,D,U)
	,dw_ins_date		date				--数据仓库插入日期
) with(appendonly=true,compresslevel=5)
distributed by(member_id);
 
3.1.5 数据加载
greenplum数据加载方式包括:SQL的insert、copy、外部表、gpload、web external table几种方式。
- insert
加载方式效率最差,只适合加载极少量数据 
insert into public.member_delta values('mem006','13100000006','I',date'2011-12-03');
insert into public.member_delta values('mem002','13100000002','D',date'2011-12-03');
insert into public.member_delta values('mem003','13100000003','U',date'2011-12-03');
 
- copy
copy这种数据加载方式源于PostgreSQL,数据需要通过Master节点,无法实现并行高效数据加载。 
copy public.member_fatdt0_1_prt_p30001231 from '/home/gpadmin/member_his_init.dat' with delimiter ',';
 
- 外部表
首先,启动gpfdist服务: 
nohup gpfdist -d /home/gpadmin/data/ -p 8888 -l /home/gpadmin/data/gpfdist.log &
 
其次,创建外部表:
drop external table if exists public.member_ext;
create external table public.member_ext
(
	Member_id			varchar(64)
	,phoneno			varchar(20)
	,action				char(1)
	,dw_ins_date		date
)
location ('gpfdist://localhost:8888/member_delta.dat')
format 'text'
(delimiter ',' null as '' escape 'off')
encoding 'gb18030'
log errors into member_err segment reject limit 2 rows;
 
最后,执行数据装载:
Insert into public.member_delta select * from public.member_ext;
 
-  
gpload
gpload是对外部表的一层封装,编写yam文件用于说明输入输出,之后执行gpload命令。 -  
可执行外部表
不用启动gpfdist服务,其中基于操作系统命令读取数据文件的方式和普通外部表类似。下面的外部表只在Master上执行: 
drop external web table if exists public.member_ext;
create external web table public.member_ext
(
	Member_id			varchar(64)
	,phoneno			varchar(20)
	,action				char(1)
	dw_ins_date			date
)
execute 'cat /home/gpadmin/data/member_delta.dat' on master
format 'text'
(delimiter ',' null as '' escape 'off')
encoding 'gb18030'
;
 
清空member_delta表并插入数据:
truncate table public.member_delta;
Insert into public.member_delta select * from public.member_ext;
 
3.1.6 数据刷新
- 拉链表刷新
step1:对事实表中的最新数据(分区P30001231)与member_delta表中的更新、删除数据进行左外连接,关联上则说明该数据发生过变更,需要将该数据的失效时间更新为当天,并插入到member_tmp0表中的历史数据分区中,关联不上则说明没有发生过变更,需要将该数据插入到member_tmp0表的当前数据分区中。Greenplum会根据dtype的数据自动选择对应的分区。 
初始全量数据为2011-12-01号,在12月3号刷新12月2号增量数据,代码如下:
truncate table public.member_tmp0;
-- 清理临时表
insert into public.member_tmp0
(
	Member_id
	,phoneno
	,dw_beg_date
	,dw_end_date
	,dtype
	,dw_status
	,dw_ins_date
)
select a.Member_id
		,a.phoneno
		,a.dw_beg_date
		,case when b.Member_id is null then a.dw_end_date
		else date'2011-12-02'
		end as dw_end_date
		,case when b.Member_id is null then 'C'
		else 'H'
		end as dtype
		,case when b.Member_id is null then a.dw_status
		else b.action 
		end as dw_status
		,date'2011-12-03'
from	public.member_fatdt0
left join public.member_delta
on		a.Member_id = b.Member_id
and 	b.action IN('D','U')
where a.dw_beg_date <= cast('2011-12-02' as date)-1
and   a.dw_end_date>cast('2011-12-02' as date) -1;
 
step2: 将member_delta的新增、更新数据插入到member_tmp0表的当前数据分区中。
insert into public.member_tmp0
(
	Member_id,
	phoneno,
	dw_beg_date,
	dw_end_date,
	dtype,
	dw_status,
	dw_ins_date
)
select Member_id,
	   phoneno,
	   cast('2011-12-02' as date),
	   cast('3000-12-31' as date),
	   'C',
	   action,
	   cast('2011-12-03' as date)
from public.member_delta
where action in('I','U');
 
step3: 将member_fatdt0表中的对应分区(P20121201)与member_tmp0表的历史数据分区交换。
Truncate table public.member_tmp1;
alter table public.member_tmp0 exchange partition for('H') with table public.member_tmp1;
alter table public.member_fatdt0 exchange partition for('2011-12-02') with table public.member_tmp1;
 
step4: 将member_fatdt0表中对应的当前数据分区(p30001231)与member_tmp0表的当前数据分区交换
alter table public.member_tmp0 exchange partition for('C') with table public.member_tmp1;
alter table public.member_fatdt0 exchange partition for('3000-12-31') with table public.member_tmp1;
 
3.1.7 分区裁剪
只对某个分区的数据进行查询
3.1.8 数据导出
GP再处理大数据量数据导出时常用的方式主要有并行导出(可写外部表)和非并行导出(COPY),copy命令比较简单。下面是可写外部表数据导出方式,通过gpfdist可写外部表将数据导出至文件服务器
 1)创建可写外部表
create writable external table member_tmp1_upload
(like member_tmp1)
location ('gpfdist://localhost:8080/member_tmp1.dat')
format 'TEXT' (DELIMITER ',')
DISTRIBUTED BY (member_id);
 
2)执行数据导出
insert into member_tmp1_upload select * from member_tmp1;
 
3)验证生成的文件
less member_tmp1.dat










