Массовая вставка в базу данных Oracle: что лучше: цикл FOR Cursor или простой выбор? - PullRequest
24 голосов
/ 12 июня 2009

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

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

Есть какая-то конкретная причина, по которой кто-то будет лучше?

Ответы [ 8 ]

29 голосов
/ 12 июня 2009

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

22 голосов
/ 15 июня 2009

Общее правило: если вы можете сделать это с помощью одного оператора 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 для каждого столбца.

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

5 голосов
/ 12 июня 2009

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

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

5 голосов
/ 12 июня 2009

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

3 голосов
/ 16 июля 2011

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

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

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

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

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

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;

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

0 голосов
/ 16 декабря 2018

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

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

CREATE TABLE sales_dataTemp as select приведение (столбец 1 как Дата) как SALES_QUARTER, приведение (продажи как число) как SALES_IN_MILLIONS, .... ОТ TABLE1;

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

0 голосов
/ 03 февраля 2013

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

Массовый сбор вместе с 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, вы включаете предложение BULK COLLECT в оператор SELECT вместо использования INTO.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...