java - 截断表表最优解
问题描述
我创建了一个 spring 集成工作流,将数据从 csv 加载到 oracle 数据库。这是一个集群环境,每个节点处理一个 csv 文件并将数据加载到临时表中。
临时表的结构:(索引上AccountNumber
)
ID
AccountNumber
ItemId
Value
我有一个将文件名发布到队列的 spring 集成 rabbitmq 配置。集群中的每个节点只提取一个文件,从文件系统(共享文件系统和 oracle 数据库)中读取 csv 并将数据加载到 TEMP 表中。(每个 csv 的大小为 2GB)。
在加载临时表中的所有数据后,一个节点应该将数据从临时表移动到与临时表具有相同结构的主表。
主表:(索引Account Number
)
Id
AccountNumber
ItemId
Value
我创建了一个存储过程,它从主表中删除现有帐号并在主表中加载帐户。
将数据移至主表后,我将在过程结束时从临时表中删除数据。
我的问题是截断此表的最佳方法是什么。
问题:假设我的主表中有这条记录。
主表:
Account Number ItemId ItemValue
-----------------------------------
123456 5 XYZ
123456 6 ABC
123456 7 DEF
现在我从 csv 获取这个条目到临时表:
AccountNumber ItemId ItemValue
------------------------------------
123456 5 FGH
现在我的主表应该只有一个值。应删除 ItemId 为 6 和 7 的行。
Account Number ItemId ItemValue
-------------------------------------
123456 5 FGH
我可以通过合并来实现这一点吗?
方案 1:
在将数据加载到 TEMP 表之前截断该表会更好吗?(两个独立的数据库事务,一个用于截断表,另一个用于数据移动)。
(这会在发布队列中的文件名之前调用)一个在加载前批量清理临时表的过程。
步骤1:
create or replace procedure CleanTempTable
IS
v_numberRows int :=20000;
BEGIN
loop
Delete from TEMP where rownum <= v_numberRows;
EXIT WHEN SQL%ROWCOUNT = 0;
commit;
END LOOP;
END;
/
将数据从 temp 移动到 main 的一种过程。
这要求在巩固阶段结束。
CREATE OR REPLACE PROCEDURE LOAD_DATA_TO_CONSOLIDATE (updatecount OUT NUMBER )
IS
cnt number := 0;
account_num MAIN_TABLE.ACCOUNT_NO%TYPE;
CURSOR account_cursor IS
SELECT distinct ACCOUNT_NO from TEMP_TABLE;
BEGIN
OPEN account_cursor;
LOOP
FETCH account_cursor INTO account_num;
EXIT WHEN account_cursor%NOTFOUND;
delete from MAIN where ACCOUNT_NO = account_num;
insert into MAIN(ID,ACCOUNT_NO,FACT_ID,FACT_VALUE) select HIBERNATE_SEQUENCE.nextval,temp.ACCOUNT_NO,temp.VALUE from TEMP temp
where ACCOUNT_NO = account_num;
cnt := cnt + sql%rowcount;
commit;
END LOOP;
updatecount := cnt;
CLOSE account_cursor;
END LOAD_DATA_TO_CONSOLIDATE;
场景二:
将数据从 TEMP 表移动到主表后截断该表会更好吗?(一个存储过程中的所有内容(在一个 DB 事务中))
CREATE OR REPLACE PROCEDURE LOAD_DATA_TO_CONSOLIDATE (updatecount OUT NUMBER )
IS
cnt number := 0;
account_num MAIN_TABLE.ACCOUNT_NO%TYPE;
CURSOR account_cursor IS
SELECT distinct ACCOUNT_NO from TEMP_TABLE;
BEGIN
OPEN account_cursor;
LOOP
FETCH account_cursor INTO account_num;
EXIT WHEN account_cursor%NOTFOUND;
delete from MAIN where ACCOUNT_NO = account_num;
insert into MAIN(ID,ACCOUNT_NO,FACT_ID,FACT_VALUE) select HIBERNATE_SEQUENCE.nextval,temp.ACCOUNT_NO,temp.VALUE from TEMP temp
where ACCOUNT_NO = account_num;
cnt := cnt + sql%rowcount;
commit;
END LOOP;
updatecount := cnt;
CLOSE account_cursor;
delete from TEMP; //removing all data
END LOAD_DATA_TO_CONSOLIDATE;
解决方案
从我的 Oracle 角度来看,您建议的选项都不是。原因如下:
- 通过首先将数据加载到临时表中,然后将其复制到主表中,您将工作加倍
- 逐行处理是缓慢的
- 使用循环提交可能会导致 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;
或者-这可能会表现最好-您可以为表中已经存在的UPDATE
值而仅不存在的行;使用单个快速语句(也称为upsert)代替两个命令ACCOUNT_NO
INSERT
MERGE
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, ...);
使用MERGE
,您无需在此处加载、从那里删除、从此处插入到那里……非常整洁,正如我所说,速度很快。根本不需要 PL/SQL。
推荐阅读
- c# - 在wpf中使用视频作为网格的背景
- amazon-web-services - 跨账户 AWS CodePipeline 无法访问 CloudFormation 部署工件
- java - 从其他目录运行 gradle 命令
- .net - Dotnet Core Nuget 集代理
- angular - 可观察的 AngularFire 存储任务永远不会完成
- python-2.7 - 定义来自第 3 方 API JSON 响应的 Cloud Endpoints 响应消息
- kotlin - Kotlin 数据类 + Gson:可选字段
- c++ - 函数中特定行的模板
- python - Snakemake中输入函数的并行输出
- python - 在 RaspBerryPI 上的 virtualenv 中安装 Scipy 的问题