处理大量的插入语句



我在一个文件中有一个巨大的插入语句列表,如下所示,我需要使用存储过程或liquibase脚本在生产数据库中执行这些语句。如果任何一条记录插入失败,那么所有内容都应该回滚。在这里,性能也很重要。

insert into employee (empid, empname, slary) values (1, 'bar', 2000);
insert into employee (empid, empname, slary) values (2, 'foo', 2000);
.
.
insert into employee (empid, empname, slary) values (100000, 'baz', 2000);

使用UNION ALL技巧每次插入100行,并用BEGINEND包装整个代码。这比生成一个简单的插入列表要棘手一些,但是批处理操作减少了逐行开销,通常可以将性能提高10倍左右。

begin
insert into employee (empid, empname, slary)
select 1, 'bar', 2000 from dual union all
select 2, 'foo', 2000 from dual union all
...
select 100, 'foobar', 2000 from dual;
...
insert into employee (empid, empname, slary)
...
select 100000, 'baz', 2000 from dual;
end;
/

我使用100行,因为如果您批量处理所有行到单个INSERTOracle可能需要太长时间来解析庞大的SQL语句。使用BEGINEND可以使Oracle在网络上一次传递整个块,并且还可以确保一条语句中的单个错误将回滚其他所有内容。

您不需要直接路径、/*+ append */提示、删除和重新创建索引、锁定、SQL Loader或任何这些花哨的功能。对于大量的数据来说,这些都是好主意。但是你实际上得到了一个小的数据量,只是一个巨大的语句。您需要针对网络延迟和SQL解析进行优化——加载数据的实际时间可能无关紧要。

可以用简单的plsql块进行包装。例如:

begin
-- all your inserts go here without any changes
insert into employee (empid, empname, slary) values (1, 'bar', 2000);
insert into employee (empid, empname, slary) values (2, 'foo', 2000);
.
.
insert into employee (empid, empname, slary) values (100000, 'baz', 2000);
--commit once in the end
commit;
end;

这样,如果有任何失败,整个事务(包括所有插入)将自动回滚。

从性能的角度来看,您将节省新工作往返的时间,因为只有一次调用db,其中包含所有插入。不过,演出不会是最好的。普通插入不是在数据库中加载数据的最快方式。使用sqldr(外部实用程序)实现批量负载和最佳性能。

提高性能:

  • 禁用或删除employees表上的所有索引和约束。您可以稍后重新创建它们。
    • 注意如果您的数据没有排序,这可能是危险的。返回索引可能需要很长时间。
  • 插入
  • 时锁定表
  • 批量插入
  • 查看Oracle的批量操作实用程序
    • 查看如何使用bcp工具与oracle dB或任何其他更好的选择为其他实用程序

对于回滚,可以使用ROLLBACK事务

最新更新