假设我有两个表 source
和 destination
。源表随着时间的推移而变化,例如从一天到另一天。举个例子,我将在此处创建两个表 source_before
和 source_after
但实际上,这是同一个表,其中随着时间的推移会发生一些 DML 语句。
create table source_before (
id int,
name varchar2(40),
creation_time date
);
insert all
into source_before values (1,'bola','01-Jan-20')
into source_before values (2,'gol','02-Jan-21')
into source_before values (3,'cav','02-Jan-23')
into source_before values (4,'bhf','02-Jan-25')
select * from dual;
select * from source_before;
1 bola 01-JAN-20
2 gol 02-JAN-21
3 cav 02-JAN-23
4 bhf 02-JAN-25
create table source_after (
id int,
name varchar2(40),
creation_time date
);
insert all
into source_after values (1,'bola','01-Jan-20')
into source_after values (2,'gol','02-Jan-21')
into source_after values (5,'zzz','02-Jan-28')
into source_after values (6,'sss','02-Jan-25')
select * from dual;
select * from source_after;
1 bola 01-JAN-20
2 gol 02-JAN-21
5 zzz 02-JAN-28
6 sss 02-JAN-25
现在假设有一个 destination
表在 source_before
存在时已更新,因此包含与 source_before
相同的数据.
create table destination (
id int,
name varchar2(40),
creation_time date
);
insert all
into destination values (1,'bola','01-Jan-20')
into destination values (2,'gol','02-Jan-21')
into destination values (3,'cav','02-Jan-23')
into destination values (4,'bhf','02-Jan-25')
select * from dual;
1 bola 01-JAN-20
2 gol 02-JAN-21
3 cav 02-JAN-23
4 bhf 02-JAN-25
现在,如果我想将 destination
更新为 source_after
创建的新更改,即删除 id 3 和 4
以及插入id 5和6
。我可以用这个语句来执行插入操作,但不能执行删除操作。
merge into destination d
using (select * from source_after) sa on (d.id = sa.id)
when matched then update
set
d.name = sa.name,
d.creation_time = sa.creation_time
when not matched then
insert (
d.id,
d.name,
d.creation_time
)
values
(
sa.id,
sa.name,
sa.creation_time
);
select * from destination;
1 bola 01-JAN-20
2 gol 02-JAN-21
3 cav 02-JAN-23
4 bhf 02-JAN-25
6 sss 02-JAN-25
5 zzz 02-JAN-28
我们可以看到行5
和6
已插入,但该语句无法删除行3
和 4.
。因此,目标是反射(reflect)更改并能够捕获 source
表上的插入和删除。因此,destination
的结果应该是:
1 bola 01-JAN-20
2 gol 02-JAN-21
5 zzz 02-JAN-28
6 sss 02-JAN-25
最佳答案
您将需要一种或另一种数据库链接,或者您将需要像 NiFi 或类似的中间件解决方案来充当中介。
在任何一种情况下,如果使用数据库链接,您都需要合并来复制插入和更新(例如示例中的操作),并需要一个单独的删除命令来处理删除。您无法通过一个命令完成所有操作。如果您无法修改目标数据库的结构,那么您必须从 Oracle 源数据库创建一个链接,并使用类似这样的内容(不保证性能):
merge into destination@dblink d
using (select * from source_after) sa on (d.id = sa.id)
when matched then update
set
d.name = sa.name,
d.creation_time = sa.creation_time
when not matched then
insert (
d.id,
d.name,
d.creation_time
)
values
(
sa.id,
sa.name,
sa.creation_time
);
delete from destination@dblink where id not in
(select id from source_after);
如果您不想要直接的数据库链接(这并不总是可行或不切实际),则可以在源数据库中使用触发器来记录各种事务及其详细信息,例如审计表中的相关列值(Oracle 不支持在最新版本中没有方便的更改数据捕获功能,因此您必须自己制作)。然后使用 NiFi 等外部工具读取该表,将审核记录转换为可以排队并应用于目标数据库的单独 DML 命令。使用 NiFi 有许多不同的方法可以做到这一点,其他数据管道工具也可以做同样的事情,所以我无法详细说明,但希望您能明白。
此外,如果使用像 NiFi 这样的外部工具,那么预计同步过程在某些时候会失败并且您需要对目标进行完全刷新的可能性:规划一种可以进行完全刷新的机制(在事实上首先这样做)以及随着时间的推移保持增量更新。
关于sql - 检测表中随时间变化的已删除行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75007899/