如何在 pl/sql oracle 中在 5 分钟内插入 100 万条数据?
Posted
技术标签:
【中文标题】如何在 pl/sql oracle 中在 5 分钟内插入 100 万条数据?【英文标题】:How to insert 1 million data less 5 minute in pl/sql oracle? 【发布时间】:2019-01-04 06:18:00 【问题描述】:需要在 5 分钟内插入大量记录。这是我尝试过的pl/SQL,
procedure insert_student(name_ in varchar2,
address_ in varchar2,
phone_ in varchar2,
class_ in varchar2) is
begin
insert into student.student_scholarship(name, address,
phone, class, date)
values (name_, address_, phone_, class_, sysdate)
);
commit;
end insert_student;
【问题讨论】:
你试过了吗?你有什么基准吗? 对于大型数据集,它首选使用 INSERT AS SELECT 或 FORALL。 (您可以使用 APPEND 提示提示 INSERT) 指定您的数据源,因为如果您不这样做,您可能会得到很多不同且不相关的答案。如果您想获得性能,您需要的是某种 BULK 操作。基本上,PL/SQL 将数据存储在 RAM 中,并将其用作批量插入行的快速方法。看看它,自己尝试一下,然后在这里举个例子。 我的朋友对这些东西很好' sqlload docs.oracle.com/cd/B19306_01/server.102/b14215/ldr_concepts.htm 要插入的百万行是否来自另一个表、一个平面文件、另一个数据库?适合您的答案将取决于数据源。 5 分钟一百万应该不会太具有挑战性。 【参考方案1】:对于下面的模拟,我们让 A 生成了一个 STUDENT 表,其中包含 1,000,000 行,填充了随机字符串。由于您没有告诉我们从哪里加载数据,我们已B将数据导出/卸载到 CSV 文件,C 通过 EXTERNAL 表使用数据,我们然后利用各种 INSERT 技术。 (全部使用 Oracle 12c,“Developer Days”VM)
A“源表”
create table student ( name, address, phone, sclass )
as
select
dbms_random.string( 'x', 25 )
, dbms_random.string( 'x', 40 )
, dbms_random.string( 'x', 20 )
, dbms_random.string( 'x', 5 )
from dual
connect by level <= 1000000 ;
-- Elapsed: 00:03:25.032
-- quick check
select count(*) from student ;
COUNT(*)
----------
1000000
B 将 1,000,000 行写入 CSV 文件
set term off
set feed off
set sqlformat csv
spool /home/oracle/data_out/out.csv
select /*+ parallel */* from student ;
spool off
C 外部表
create table external_ (
name varchar2( 4000 )
, address varchar2( 4000 )
, phone varchar2( 4000 )
, sclass varchar2( 4000 )
)
organization external (
type oracle_loader
default directory external_tables
access parameters
(
records field names all files
fields CSV with embedded record terminators
)
location
(
'out.csv'
)
)
/
-- quick check
SQL> select count(*) from external_ ;
COUNT(*)
----------
1000000
“目的地”表
create table scholarship (
name varchar2( 25 )
, address varchar2( 40 )
, phone varchar2( 20 )
, sclass varchar2( 5 )
, sdate date default sysdate
);
当使用纯 SQL 插入 1,000,000 行时,我们得到以下时间(测试运行 3 次,SCHOLARSHIP 表在测试之间被 DROPped)。
-- 1 SQL: INSERT ... SELECT ...
insert into scholarship ( name, address, phone, sclass )
select name, address, phone, sclass from external_ ;
-- 1,000,000 rows inserted.
-- Elapsed: 00:00:02.607
-- Elapsed: 00:00:02.300
-- Elapsed: 00:00:02.473
可能最糟糕的选择是:使用 PL/SQL 和 CURSOR FOR LOOP(测试运行 3 次,SCHOLARSHIP 在两次测试之间中断)。
--2 PL/SQL: use a cursor for loop ("slow by slow")
begin
for rec_ in ( select * from external_ )
loop
insert into scholarship ( name, address, phone, sclass )
values ( rec_.name, rec_.address, rec_.phone, rec_.sclass ) ;
end loop ;
commit ;
end ;
/
-- PL/SQL procedure successfully completed.
-- Elapsed: 00:00:24.777
-- Elapsed: 00:00:22.700
-- Elapsed: 00:00:24.291
更好一点:使用 PL/SQL 的批量操作(同样使用“经过时间”进行 3 次测试运行)。
--3 PL/SQL: use BULK COLLECT and FORALL (no need to re-compile in between tests)
create or replace procedure insert_students is
type student_t is table of external_%rowtype index by pls_integer ;
lstudents student_t ;
begin
select * bulk collect into lstudents from external_ ;
forall i in 1.. lstudents.count
insert into scholarship ( name, address, phone, sclass )
values ( lstudents( i ).name, lstudents( i ).address, lstudents( i ).phone, lstudents( i ).sclass );
end ;
/
begin
insert_students ;
commit ;
end ;
/
-- PL/SQL procedure successfully completed.
-- Elapsed: 00:00:08.706
-- Elapsed: 00:00:06.762
-- Elapsed: 00:00:04.989
正如许多人会告诉您的那样:尽可能使用 SQL(仅)。现在,您可能会看到您的初始方法 - 使用带有参数的过程,并且一次只执行一次 INSERT - 可能不是解决问题的最佳技术。
【讨论】:
【参考方案2】:使用FORALL
语句。它比使用FOR
循环逐一保存记录要快得多,因为它不会在每次 PL/SQL 处理器通过 SQL 语句时更改 PL/SQL 和 SQL 之间的上下文。
CREATE TABLE students (
id NUMBER(19,0),
address VARCHAR2(300)
);
/
CREATE OR REPLACE PACKAGE pack AS
TYPE t_students IS TABLE OF students%ROWTYPE INDEX BY BINARY_INTEGER;
PROCEDURE insert_students( l_students IN t_students);
END pack;
/
CREATE OR REPLACE PACKAGE BODY pack AS
PROCEDURE insert_students( l_students IN t_students) AS
BEGIN
FORALL i IN 1..l_students.COUNT
INSERT INTO students VALUES (l_students(i).id, l_students(i).address);
END;
END pack;
/
【讨论】:
FORALL 比单个调用快,但可能不比纯 SQLinsert into ... select * from ...
快。 OP 没有提供有关其输入数据来源的详细信息,因此我们没有理由认为他们根本需要任何 PL/SQL。以上是关于如何在 pl/sql oracle 中在 5 分钟内插入 100 万条数据?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Oracle (PL/SQL) 动态 sql 将数据查询到 %rowtype 变量中
oracle创建存储过程时,提示错误是:错误(5,18): PL/SQL: ORA-00947: 没有足够的值?代码如下: