Java 截断表最优解
我创建了一个spring集成工作流,将数据从csv加载到oracle数据库。这是一个集群环境,其中每个节点处理一个csv文件并将数据加载到临时表中 临时表的结构:(在Java 截断表最优解,java,oracle,performance,plsql,spring-integration,Java,Oracle,Performance,Plsql,Spring Integration,我创建了一个spring集成工作流,将数据从csv加载到oracle数据库。这是一个集群环境,其中每个节点处理一个csv文件并将数据加载到临时表中 临时表的结构:(在AccountNumber上的索引) 我有一个spring集成rabbitmq配置,它将文件名发布到队列。集群中的每个节点只拾取一个文件,从文件系统(共享文件系统和oracle数据库)读取csv,并将数据加载到临时表中。(每个csv的大小为2GB) 加载临时表中的所有数据后,一个节点应将数据从临时表移动到与临时表具有相同结构的主表
AccountNumber
上的索引)
我有一个spring集成rabbitmq配置,它将文件名发布到队列。集群中的每个节点只拾取一个文件,从文件系统(共享文件系统和oracle数据库)读取csv,并将数据加载到临时表中。(每个csv的大小为2GB)
加载临时表中的所有数据后,一个节点应将数据从临时表移动到与临时表具有相同结构的主表
主表:(关于账号的索引
)
我创建了一个存储过程,它从主表中删除现有的帐号,并在主表中加载帐号
一旦数据移动到主表,我将在过程结束时从临时表中删除数据
我的问题是截断此表的最佳方式是什么
问题:假设我的主表中有这个记录
主表:
账号ItemId ItemValue
-----------------------------------
123456 5 XYZ
123456 ABC
123456 7 DEF
现在,我从csv到temp表中获取以下条目:
AccountNumber ItemId ItemValue
------------------------------------
123456 5 FGH
现在我的主表应该只有一个值。应删除ItemId为6和7的行
账号ItemId ItemValue
-------------------------------------
123456 5 FGH
我可以通过合并实现这一点吗
场景1:
在将数据加载到临时表之前截断此表是否更好?(两个独立的数据库事务,一个用于截断表,另一个用于数据移动)
(这将在队列中发布文件名之前调用)
加载前分批清理临时表的一个过程
步骤1:
创建或替换过程可清除
是
v_numberRows int:=20000;
开始
环
从临时删除,其中rownum从我的Oracle ish角度来看,您建议的两个选项都没有。原因如下:
- 通过首先将数据加载到临时表中,然后将其复制到主表中,可以将作业加倍
- 逐行处理是一步一步地慢
- 使用循环提交可能会导致ORA-01555错误
- 删除(
DELETE
命令)总是比截断(TRUNCATE
)慢
建议:为了避免使用临时表,请使用外部表功能,该功能使用CSV文件,就像使用普通Oracle表一样
这意味着一切都可以在两个声明中完成:
-- Delete rows from the MAIN table whose ACCOUNT_NO exists in the CSV file
delete from main m
where exists (select null
from external_table t
where t.account_no = m.account_no
);
-- Insert rows into the MAIN table
insert into main (col1, col2, ...)
select col1, col2 from external_table;
或者-哪一种可能执行得最好-您可以更新表中已存在的科目号
的值,并且仅插入不存在的行;不要使用两个命令,而是使用一个快速的MERGE
语句(也称为upsert)
使用MERGE
,您不需要在此处加载、从此处删除、从此处插入到此处。。。非常整洁,就像我说的,很快。根本不需要PL/SQL。从我的Oracle ish观点来看,您建议的两个选项都没有。原因如下:
- 通过首先将数据加载到临时表中,然后将其复制到主表中,可以将作业加倍
- 逐行处理是一步一步地慢
- 使用循环提交可能会导致ORA-01555错误
- 删除(
DELETE
命令)总是比截断(TRUNCATE
)慢
建议:为了避免使用临时表,请使用外部表功能,该功能使用CSV文件,就像使用普通Oracle表一样
这意味着一切都可以在两个声明中完成:
-- Delete rows from the MAIN table whose ACCOUNT_NO exists in the CSV file
delete from main m
where exists (select null
from external_table t
where t.account_no = m.account_no
);
-- Insert rows into the MAIN table
insert into main (col1, col2, ...)
select col1, col2 from external_table;
或者-哪一种可能执行得最好-您可以更新表中已存在的科目号
的值,并且仅插入不存在的行;不要使用两个命令,而是使用一个快速的MERGE
语句(也称为upsert)
使用MERGE
,您不需要在此处加载、从此处删除、从此处插入到此处。。。非常整洁,就像我说的,很快。根本不需要PL/SQL。为什么要使用临时表?既然使用的是spring integration,请逐行读取文件(因为它们是2gb文件),并发送每一行进行处理。您可以让队列中的多个使用者获取文件。是的,我在每个节点上配置了rabbitmq侦听器,每个节点接收一个csv文件->解析它->在临时表中加载数据。csv 1->1节点->解析它->临时表csv 2->2节点->解析它->最后的临时表任意一个节点->使用前面提到的store proc将数据从临时表合并到主表。我不发送行,而是发送一个文件名..进一步从文件系统读取csv。你的意思是我应该批量处理csv行,rabbitmq使用者应该读取行而不是文件?是的,我的意思是你可以让你的使用者逐行读取文件,然后发送下一个通道以将行转换为值对象,然后如果你需要对另一个通道进行一些处理,否则,只需将value对象传递给保存它的DAL即可。我只是不明白你为什么用临时表?为什么不直接将文件保存到主表中?为什么使用临时表?既然使用了spring integration,请逐行读取文件(因为它们是2gb文件),并发送每一行进行处理。您可以让队列中的多个使用者获取文件。是的,我有rabb的配置
-- Delete rows from the MAIN table whose ACCOUNT_NO exists in the CSV file
delete from main m
where exists (select null
from external_table t
where t.account_no = m.account_no
);
-- Insert rows into the MAIN table
insert into main (col1, col2, ...)
select col1, col2 from external_table;
merge into main m
using (select t.account_no, t.col1, t.col2, ...
from external_table t
) x
on m.account_no = x.account_no
when matched then update set m.col1 = x.col1,
m.col2 = x.col2, ...
when not matched then insert (account_no, col1, col2, ...)
values (x.account_no, x.col1, x.col2, ...);