什么是拉链表?
简单点说:记录一个事物,从开始一直到当前状态的所有变化信息。
复杂点说:记录数据在某一时间内的状态,以及数据在某一时间点的变化的数据存储方式。
为什么需要拉链表?
针对一些数据量大的表,像是保存了几年的数据,但是新增或者需要操作查看的数据只有一部分,或者只需要某个时间节点的数据,这个时候就可以使用拉链表进行操作。
怎么编写拉链表代码?
- 创建一个拷贝了emp的临时表emp1,防止修改到emp源表。
sql
drop table emp1;--如果以前有
alter table emp1 add up_date date; --添加了一个新增数据的时间节点字段
insert into emp1 e1(
e1.empno,
e1.ename,
e1.job,
e1.mgr,
e1.hiredate,
e1.sal,
e1.comm,
e1.deptno,
e1.up_date
)
select
et.empno,
et.ename,
et.job,
et.mgr,
et.hiredate,
et.sal,
et.comm,
et.deptno,
sysdate-5
from emp et;
select * from emp1
- 创建目标表,用来比对emp1的数据
重要字段start_date 、end_date 主要是这两个字段提取一段时间的数据。
sql
---目标表
drop table linkTable;
create table linkTable as select * from emp where 1=2;
select * from linkTable;
alter table linkTable add up_date date;
alter table linkTable add start_date date;--开始时间
alter table linkTable add end_date date;--结束时间
insert into linkTable lt(
lt.empno,
lt.ename,
lt.job,
lt.mgr,
lt.hiredate,
lt.sal,
lt.comm,
lt.deptno,
lt.up_date,
lt.start_date,
lt.end_date
)
select
et.empno,
et.ename,
et.job,
et.mgr,
et.hiredate,
et.sal,
et.comm,
et.deptno,
et.up_date,
to_date('2024-4-26','YYYY-MM-DD'),
to_date('9999-9-9','YYYY-DD-MM')
from emp1 et;
- 手动模拟新增的emp1数据
sql
update emp1 set sal=sal+1000,up_date=sysdate where ename='SMITH';
insert into emp1 values(9999,'张三','xingjiapo',7369,
date'1998-06-01',3600,1000,10,sysdate-1);
select * from emp1;
- 创建一个存储过程,封装更新数据的动作。
开链:目标中没有改记录,新增插入该条记录并把结束时间改成无穷大。
闭链:目标中存在该条记录,只是部分状态值不同,update该数据,并把end_date调整为当日时间。
sql
---创建存储过程
create or replace procedure updateLinkTable(startTime varchar2,endTime varchar2)
is
v_start date := to_date(startTime,'YYYY-MM-DD HH24:MI:SS');
v_end date := to_date(endTime,'YYYY-MM-DD HH24:MI:SS');
cursor update_cursor is --创建游标保存指定时间内需要比对的数据
select * from emp1
where up_date>=v_start and up_date<=v_end; ---指定时间内的数据
v_count number;
begin
for i in update_cursor loop --目标表与源表比对,
select count(1) into v_count from linkTable lk --找到当前的数据并且当前数据未过期
where i.empno = lk.empno and lk.end_date=to_date('9999-9-9','YYYY-MM-DD');
if v_count=1 then
update linkTable lk set lk.end_date=trunc(sysdate-1) --把原先的数据更新为过期数据(历史数据) 也就是闭链
where i.empno = lk.empno and lk.end_date=to_date('9999-9-9','YYYY-MM-DD');
--添加当前已更新的业务变动数据,并更新end_date无穷大
insert into linkTable lk(
lk.empno,
lk.ename,
lk.job,
lk.mgr,
lk.hiredate,
lk.sal,
lk.comm,
lk.deptno,
lk.up_date,
lk.start_date,
lk.end_date
) values(
i.empno,
i.ename,
i.job,
i.mgr,
i.hiredate,
i.sal,
i.comm,
i.deptno,
i.up_date,
trunc(sysdate),
to_date('9999-9-9','YYYY-MM-DD')
);
elsif v_count=0 then --没有匹配上说明当前数据是新增数据,直接插入进去即可 (更新end_date无穷大 开链)
insert into linkTable lk(
lk.empno,
lk.ename,
lk.job,
lk.mgr,
lk.hiredate,
lk.sal,
lk.comm,
lk.deptno,
lk.up_date,
lk.start_date,
lk.end_date
) values(
i.empno,
i.ename,
i.job,
i.mgr,
i.hiredate,
i.sal,
i.comm,
i.deptno,
i.up_date,
trunc(sysdate),
to_date('9999-9-9','YYYY-MM-DD')
);
end if;
end loop;
exception
when others then
dbms_output.put_line(sqlcode);
dbms_output.put_line(sqlerrm);
end;
select * from linkTable;
select * from emp1;
- 调用存储过程,更新指定时间数据。
sql
---方法一 pl/sql调用
begin
updateLinkTable('2024-5-28 00:00:00','2024-5-29 23:59:59');
end;
--方法二 SQL语句调用
call updateLinkTable(p_star => '2024-05-28 00:00:00',p_end => '2024-05-29 23:59:59');
-- 或者
call updateLinkTable('2024-05-28 00:00:00','2024-05-29 23:59:59');
拉链表的优缺点
优点
- 节省存储空间,避免没必要的重复数据
- 能查询到历史快照
缺点
- 内存占用,查询效率受拉链长度的影响
- 断链难以恢复