Вставка миллиона записей

  • Автор темы AntonStarkov
  • Дата начала
A

AntonStarkov

Гость
#1
Приветствую всех!
Помогите решить задачку:

Есть таблица около 150 атрибутов, один UNIQUE остальные нет. В ней около 50 миллионов записей. Нужно ее увеличить примерно в двое. Каким лучше способом это сделать? Insert в цикле, хотя я и не большой знаток, думаю будет очень, очень, очень долго.
Поделитесь опытом :)
Заранее спасибо!
 
A

AntonStarkov

Гость
#3
>СУБД? Источник данных?

Oracle. Данные произвольные, можно из этой же таблицы, только с уникальным одним полем.
 
A

Aleksey

Гость
#4
1) Уникальное поле что из себя представляет? Это число или что-то другое?
2) Сколько процессоров на сервере (или ядер)?

Все зависит от размера таблицы, если бы не уникальность, то самое простое - это такой код:

Код:
INSERT /*+ APPEND */ INTO tab1
SELECT /*+ PARALLEL (t) */ *
FROM tab1 t;

COMMIT;
Чтобы поддержать уникальность можно использовать триггер и последовательность, если таковых еще нет.
 
?

????

Гость
#5
самый быстрый способ - sql*loader, но надо иметь данные для импорта

можно и на pl\sql используя bulk collect и forall
[sql]DECLARE
TYPE prod_tab IS TABLE OF products%ROWTYPE;
products_tab prod_tab := prod_tab();
BEGIN SELECT * BULK COLLECT INTO products_tab FROM products;

FORALL i in products_tab.first .. products_tab.last
INSERT INTO products VALUES products_tab(i);

COMMIT;
END;[/sql]
в примере мы выбираем все данные из таблицы products и вставляем в неё же (удваиваем данные в таблице). если уникальное поле генерируется в триггере на основе сиквенса (или любым другим способом) - надо будет только подставить имя таблицы. иначе варианты :)

ну а общеи рекомендации при вставке большого объема данных - удаление индексов и пересоздание после вставки и т.д.
 
A

Aleksey

Гость
#6
Ничего быстрее SQL нет.

Код:
INSERT /*+ APPEND */ INTO tab1
SELECT /*+ PARALLEL (t) */ *
FROM tab1 t;
В этом SQL-коде процессы параллелятся и загрузка идет в режиме direct path минуя кэш буферов. Ничего быстрее не найдешь :)

Указанный уважаемым ???? (в предыдущем посте) код PL/SQL, будет самым медленным, кроме того, в данном случае конструкция BULK COLLECT и FORALL мало того что гораздо медленней, но еще и повалится может, так как вся эта конструкция хранится в памяти сеанса (а это практически вся таблица):
Код:
TYPE prod_tab IS TABLE OF products%ROWTYPE;
products_tab prod_tab := prod_tab();
Далее там же:
...если уникальное поле генерируется в триггере на основе сиквенса (или любым другим способом) - надо будет только подставить имя таблицы.
Чтобы этот код работал нужно не только имя таблицы подставить, но и чтобы не было ограничения UNIQUE (так же, как и в моем INSERTe), так как вставка копии данных в таблицу не прокатит из-за нарушения целостности UNIQUE ключа.

sql*loader - это крутое, но не самое быстрое средство, только его готовить надо дольше, чем сама заливка будет идти и при этом надо еще поиметь что лить. ;)
Удаление и пересоздание индексов хорошо помогает, но только до тех пор, пока кусок данных, вставляемый в таблицу относительно размера самой таблицы достаточно велик. Это я к тому, что надо разумно подходить к удалению индексов.

Я почему спросил, какое поле обеспечивает уникальность, если это число, то тут никаких триггеров не надо. Сначала узнаем максимальное значение в этом поле (или же изначально берем число, которое заведомо больше максимального значения уникального ключа):
Код:
SELECT max(id) FROM tab;
Затем полученное число используем в INSERTe - прибавляя его к существующим значениям ID (пусть у нас получился максимальный ID-шник 1000):
Код:
INSERT /*+ APPEND */ INTO tab1
SELECT /*+ PARALLEL (t) */ 
id+1000, pole1,..., poleN 
FROM tab1 t;
Тут больно будет в другом:
Есть таблица около 150 атрибутов
Это значит, что надо будет все 150 полей перечислить в списке выбора оператора SELECT... ;)