Могу ли я отправить «пакетные» вставки в Oracle? - PullRequest
1 голос
/ 11 февраля 2009

Сценарий:

  • Я загружаю некоторые данные в локальную базу данных MySQL каждый день, около 2 миллионов строк;
  • Мне нужно ( иметь для - это вопрос аудита / регулирования) перейти на «правильно» администрируемый сервер, который в настоящее время выглядит как Oracle 10g;
  • Сервер находится в другой стране: ток в оба конца сети занимает 60-70 мс;
  • Входные данные - это файл CSV в денормализованной форме: я нормализую данные перед загрузкой, каждая строка обычно приводит к 3-8 вставкам в до 4 таблиц;
  • Скрипт загрузки в настоящее время реализован на Ruby с использованием ActiveRecord и fastcsv. Я пробовал гем ar-extensions, но он предполагает, что идея предложения с несколькими значениями в стиле MySQL будет работать. Это не так.

РЕДАКТИРОВАТЬ : Очень полезные ответы уже - спасибо! Подробнее об этом надоедливом входном файле. Количество полей является переменным, и позиции менялись несколько раз - мой текущий скрипт определяет контент, анализируя строку заголовка (ну, более быстрый csv и хитрый конвертер делают это). Так что прямая загрузка и пост-обработка SQL не будет работать без нескольких версий загрузочного файла, что ужасно. Также это немецкий CSV-файл: разделенный точкой с запятой (ничего страшного) и десятичные дроби, обозначенные запятыми (довольно большая сделка, если мы не загрузим как VARCHAR и текстовый процесс потом - тьфу).

Проблема:

Загрузка 2 миллионов строк со скоростью около 7 / сек займет более 24 часов! Это может быть недостатком ежедневного процесса, не говоря уже о том, что пользователи скорее хотели бы иметь возможность доступа к данным через 5 часов после того, как они станут доступны в форме CSV!

Я смотрел на применение нескольких вставок за сетевое отключение: довольно неловкий синтаксис INSERT ALL... был бы в порядке, за исключением того, что в настоящее время я применяю уникальный идентификатор для каждой строки, используя последовательность. Оказывается,

INSERT ALL
    INTO tablea (id,b,c) VALUES (tablea_seq.nextval,1,2)
    INTO tablea (id,b,c) VALUES (tablea_seq.nextval,3,4)
    INTO tablea (id,b,c) VALUES (tablea_seq.nextval,5,6)
SELECT 1 FROM dual;

(я говорил, что это было неловко?) Пытается использовать один и тот же идентификатор для всех трех строк. Похоже, что Oracle подтверждает это.

Последняя попытка - отправить несколько INSERT за одно выполнение, например ::1010 *

    INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,1,2);
    INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,3,4);
    INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,5,6);

Я не нашел способа убедить Оракула принять это.

Вопрос (ы)

  • Я что-то упустил очевидное? (Я был бы , поэтому доволен, если бы это оказалось так!)
  • Если я не могу отправить несколько вкладок, что еще можно попробовать?

Зачем это принимать?

По какой-то причине я предпочитаю сохранять свой код максимально свободным от платформо-зависимых конструкций: одна из причин, по которой возникла эта проблема, заключается в том, что я перехожу с MySQL на Oracle; Вполне возможно, что однажды может произойти еще один шаг по географическим причинам, и я не могу быть уверен в платформе. Поэтому доведение моей библиотеки базы данных до такой степени, что она может использовать текстовую SQL-команду для достижения разумного масштабирования, было привлекательным, и блок PL / SQL выполняет это. Теперь, если появится другая платформа, изменение будет ограничено заменой адаптера в коде: по всей вероятности, одной строкой.

Ответы [ 6 ]

10 голосов
/ 11 февраля 2009

Как насчет отправки файла csv на сервер oracle db, используйте SQLLoader , чтобы загрузить файл csv в промежуточную таблицу, а затем запустите хранимую процедуру для преобразования и вставки его в финальные таблицы?

4 голосов
/ 11 февраля 2009

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

insert into tablea (id,b,c)
 ( select tablea_seq.nextval,1,2 from dual union all
   select tablea_seq.nextval,3,4 from dual union all
   select tablea_seq.nextval,3,4 from dual union all
   select tablea_seq.nextval,3,4 from dual union all
   ...
 )

Это работает до примерно 1024 строк, когда я правильно помню.

Вы также можете отправить его как пакетную инструкцию PL / SQL:

BEGIN
 INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,1,2);
 INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,3,4);
 INSERT INTO tablea (id,b,c) VALUES (tablea_seq.nextval,5,6);
 ...
 COMMIT;
END
2 голосов
/ 11 февраля 2009

Я бы загружал необработанный CSV-файл в выделенную таблицу в базе данных, используя SQL * Loader, без его нормализации, а затем выполнял код для данных в таблице, чтобы нормализовать его для различных необходимых таблиц. Это сведет к минимуму количество обращений и является довольно традиционным подходом в сообществе Oracle.

SQL * Загрузчик может быть немного сложным с точки зрения начальной кривой обучения, но вы можете вскоре опубликовать, если застряли.

http://download.oracle.com/docs/cd/B19306_01/server.102/b14215/part_ldr.htm#i436326

0 голосов
/ 15 февраля 2009

SQL * Loader - это поставляемая Oracle утилита, которая позволяет загружать данные из плоского файла в одну или несколько таблиц базы данных. Это будет в 10-100 раз быстрее, чем вставки с использованием запросов.

http://www.orafaq.com/wiki/SQL*Loader_FAQ

Если SQL * Loader не обрезает его, попробуйте небольшую программу препроцессора, которая форматирует файл в читаемый формат SQL * Loader.

0 голосов
/ 13 февраля 2009

Вместо выполнения SQL по сети вы можете записать вставки в текстовый файл, переместить его по сети и запустить там локально.

0 голосов
/ 12 февраля 2009

У меня есть быстрое предложение. Я из мира MySQL, но прошел обучение на Oracle, думаю, это сработает.

В MySQL вы можете вставить несколько записей одним оператором вставки. Это выглядит так:

INSERT INTO table_name (column_one, column_two, column_three, column_four)
VALUES
    ('a', 'one', 'alpha', 'uno'),        // Row 1
    ('b', 'two', 'beta', 'dos'),         // Row 2
    ('c', 'three', 'gamma', 'tres'),     // etc.
....
    ('z', 'twenty-six', 'omega', 'veintiséis');

Теперь очевидно, что вы можете вставить только одну таблицу за раз, и вы не захотите делать 2 миллиона записей, но вы можете легко сделать 10, 20 или 100 за один раз (если вам разрешены пакеты такого большого размера). Возможно, вам придется сгенерировать это вручную, я не знаю, поддерживают ли они фреймворки, которые вы используете (или, если уж на то пошло, какую-то), для создания такого кода для вас.

В мире MySQL это ДРАМАТИЧЕСКИ ускоряет вставки. Я предполагаю, что он выполняет все обновления индекса и тому подобное одновременно, но он также предотвращает необходимость повторного анализа SQL при каждой вставке.

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


NunoG прав, что вы можете загружать CSV-файлы напрямую в Oracle. Возможно, вам лучше всего прочитать во входном файле, сгенерировать нормализованный набор файлов CSV (по одному для каждой таблицы), а затем загружать каждый из них по одному.

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