it-roy-ru.com

Массовая вставка в базу данных Oracle: что лучше: цикл FOR Cursor или простой выбор?

Что было бы лучшим вариантом для массовой вставки в базу данных Oracle? A цикл Cursor, как

DECLARE
   CURSOR C1 IS SELECT * FROM FOO;
BEGIN
   FOR C1_REC IN C1 LOOP
   INSERT INTO BAR(A,
                B,
                C)
          VALUES(C1.A,
                 C1.B,
                 C1.C);
   END LOOP;
END

или простой выбор, например:

INSERT INTO BAR(A,
                B,
                C)
        (SELECT A,
                B,
                C
        FROM FOO);

Любая конкретная причина, или кто-то будет лучше?

24
Sathyajith Bhat

Я бы порекомендовал опцию Select, потому что курсоры занимают больше времени.
Кроме того, использование Select намного проще для тех, кто хочет изменить ваш запрос.

28
Josh Mein

Общее практическое правило: если вы можете сделать это с помощью одного оператора SQL вместо PL/SQL, вам следует это сделать. Обычно это будет более эффективным.

Однако, если вам нужно добавить больше процедурной логики (по какой-то причине), вам может потребоваться использовать PL/SQL, но вы должны использовать массовые операции вместо построчной обработки. (Примечание: в Oracle 10g и более поздних версиях цикл FOR будет автоматически использовать BULK COLLECT для выборки 100 строк за раз; однако оператор вставки все равно будет выполняться построчно).

например.

DECLARE
   TYPE tA IS TABLE OF FOO.A%TYPE INDEX BY PLS_INTEGER;
   TYPE tB IS TABLE OF FOO.B%TYPE INDEX BY PLS_INTEGER;
   TYPE tC IS TABLE OF FOO.C%TYPE INDEX BY PLS_INTEGER;
   rA tA;
   rB tB;
   rC tC;
BEGIN
   SELECT * BULK COLLECT INTO rA, rB, rC FROM FOO;
   -- (do some procedural logic on the data?)
   FORALL i IN rA.FIRST..rA.LAST
      INSERT INTO BAR(A,
                      B,
                      C)
      VALUES(rA(i),
             rB(i),
             rC(i));
END;

Вышесказанное имеет преимущество минимизации переключений контекста между SQL и PL/SQL. Oracle 11g также лучше поддерживает таблицы записей, так что вам не нужно иметь отдельную таблицу PL/SQL для каждого столбца.

Кроме того, если объем данных очень велик, можно изменить код для обработки данных в пакетном режиме.

21
Jeffrey Kemp

Если ваш сегмент отката/сегмент отмены может соответствовать размеру транзакции, тогда вариант 2 лучше. Вариант 1 полезен, если у вас нет необходимой емкости отката и вам нужно разбить большую вставку на более мелкие коммиты, чтобы вы не получили сегмент отката/отмены слишком маленькие ошибки. 

5
MichaelN

Простая вставка/выбор как ваш второй вариант гораздо предпочтительнее. Для каждой вставки в первом варианте вам требуется переключение контекста с pl/sql на sql. Запустите каждый с помощью trace/tkprof и проверьте результаты.

Если, как упоминает Майкл, ваш откат не может обработать утверждение, то пусть ваш dba даст вам больше. Диск дешев, в то время как частичные результаты, полученные при вставке ваших данных за несколько проходов, потенциально довольно дороги. (Почти нет отмены, связанной со вставкой.)

5
Scott Swank

Я думаю, что в этом вопросе отсутствует одна важная информация.

Сколько записей вы вставите?

  1. Если от 1 до около. 10.000 тогда вы должны использовать оператор SQL (как они сказали, что это легко понять и легко написать).
  2. Если из ок. 10.000 к приблизительно. 100.000 тогда вы должны использовать курсор, но вы должны добавить логику для фиксации на каждые 10.000 записей. 
  3. Если из ок. 100 000 на миллионы, тогда вы должны использовать массовый сбор для лучшей производительности.
3
sulica

Как вы можете видеть, прочитав другие ответы, есть много доступных вариантов. Если вы просто делаете <10k строк, вам следует перейти ко второму варианту. 

Короче говоря, для приблизительно> 10 Кб полностью до <100 Кб. Это своего рода серая зона. Многие старые старички будут лаять на большие сегменты отката. Но, честно говоря, аппаратное и программное обеспечение значительно продвинулись в том, что вы можете выбрать вариант 2 для большого количества записей, если будете запускать код только несколько раз. В противном случае вы, вероятно, должны фиксировать каждые 1k-10k или около того строк. Вот фрагмент, который я использую. Мне это нравится, потому что оно короткое, и мне не нужно объявлять курсор. Кроме того, он имеет преимущества массового сбора и сбора.

begin
    for r in (select rownum rn, t.* from foo t) loop
        insert into bar (A,B,C) values (r.A,r.B,r.C);
        if mod(rn,1000)=0 then
            commit;
        end if;
    end;
    commit;
end;

Я нашел эту ссылку с сайта Oracle, которая иллюстрирует варианты более подробно.

2
Arturo Hernandez

Я не делаю ни для ежедневной полной перезагрузки данных. Например, я загружаю свой сайт в Денвере. Существуют и другие стратегии для дельт в реальном времени.

Я использую SQL для создания таблицы, как я обнаружил, почти так же быстро, как и массовая загрузка Например, ниже для создания данных используется оператор создания таблицы, приводящий столбцы к нужному типу данных:

CREATE TABLE sales_dataTemp как выберите Cast (столбец1 как Date) как SALES_QUARTER, Cast (продажи как номер) как SALES_IN_MILLIONS, .... FROM ТАБЛИЦА 1;

эта временная таблица точно отражает структуру моей целевой таблицы, которая является списком, разделенным по сайту . Затем я делаю обмен разделами с разделом DENVER, и у меня есть новый набор данных.

0
Hughsmg

Ты можешь использовать:

Массовый сбор вместе с FOR ALL, который называется Bulk binding

Потому что оператор PL/SQL forall работает быстрее в 30 раз для простых вставок таблиц. 

BULK_COLLECT и Oracle FORALL вместе эти две функции известны как Bulk Binding. Массовое связывание - это метод PL/SQL, в котором вместо нескольких отдельных операторов SELECT, INSERT, UPDATE или DELETE выполняются операции извлечения или хранения данных в таблице, причем все операции выполняются одновременно. Это позволяет избежать переключения контекста, которое вы получаете, когда движок PL/SQL должен перейти на движок SQL, затем обратно на движок PL/SQL и т.д., Когда вы по отдельности обращаетесь к строкам по одной. Чтобы выполнить массовое связывание с помощью операторов INSERT, UPDATE и DELETE, вы заключаете оператор SQL в оператор PL/SQL FORALL. Чтобы выполнить массовое связывание с операторами SELECT, вместо оператора SELECT вы включаете предложение BULK COLLECT в оператор INTO.

Это улучшает производительность.

0
user2001117