Как вставить 1 миллион данных менее 5 минут в оракуле pl / sql? - PullRequest
0 голосов
/ 04 января 2019

Необходимо вставить большое количество записей в течение 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; 

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Для следующего моделирования мы {A} сгенерировали таблицу STUDENT, содержащую 1 000 000 строк, заполненных случайными строками.Поскольку вы не сообщили нам , куда вы загружаете данные, мы {B} экспортировали / выгружали данные в файл CSV, а {C} использовали данные через таблицу EXTERNAL, которую мы затемиспользовать для различных методов вставки.(все сделано с помощью Oracle 12c, VM Developer Days)

{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
);

При вставке 1 000 000 строк с использованием чистого SQL мы получили следующее время (тестовый запуск 3 разатаблица SCHOLARSHIP была сброшена между тестами.

-- {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 (только) когда это возможно.Теперь вы, вероятно, видите, что ваш первоначальный подход - использование процедуры с параметрами и выполнение только одной вставки за раз - может быть не лучшим методом для решения вашей проблемы.

0 голосов
/ 04 января 2019

Использовать 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;
/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...