Задался интересным вопросом, вставки через loader таблиц master-detail.
Взял примерчик с forum.oracle.com, немного его попилил и наткнулся на проблему, что генерируемый в master table ключ получить из записи detail совсем не просто.
Итак задача: есть текстовый файл, содержащий данные из родительской и дочерней таблицы в перемешку. Нет доступа к серверу :) (что бы не было желания делать внешние таблицы) и неохота делать пост-процедуры обработки (как советует делать дядюшка Кайт). Но есть желание поизголяться с sql loader.
Пример входных данных:
Решение 1 (используя sequence)
Без OPTIONS (ROWS = 1) ничего работать не будет -- все дочерние записи привяжутся к последней записи
Решение 2 (натолкнувшее на написание этой задачи)
Допустим у нас есть какой-то признак, по которому мы можем найти родительскую запись (в примере добавил в конце строки еще поле). Будем получать по этому признаку идентификатор родителькой записи:
Но вынесем запрос для получения для идентификатора в функцию:
Такое ощущение, что тут что-то аналогичное bind array в dbms_sql -- запросы выполняются 1 раз для первой переменной массива.
Трейс, полученный в результате эксперимента (для немного измененной таблицы) для INSERT:
Взял примерчик с forum.oracle.com, немного его попилил и наткнулся на проблему, что генерируемый в master table ключ получить из записи detail совсем не просто.
Итак задача: есть текстовый файл, содержащий данные из родительской и дочерней таблицы в перемешку. Нет доступа к серверу :) (что бы не было желания делать внешние таблицы) и неохота делать пост-процедуры обработки (как советует делать дядюшка Кайт). Но есть желание поизголяться с sql loader.
Пример входных данных:
M,master1
D,master1-detail1
D,master1-detail2
D,master1-detail3
M,master2
D,master2-detail1
M,master3
D,master3-detail1
Скрипт для создания объектов:CREATE TABLE master_table(ID NUMBER,code VARCHAR2(50),creation DATE);
CREATE TABLE detail_table(pid NUMBER, NAME VARCHAR2(100));
CREATE SEQUENCE testseq INCREMENT BY 1;
Решение 1 (используя sequence)
OPTIONS (ROWS = 1)
LOAD DATA
INFILE *
TRUNCATE
INTO TABLE master_table
WHEN (1) ='M'
FIELDS TERMINATED BY ',' optionally enclosed by '"' TRAILING NULLCOLS
(id expression "testseq.nextval",
mcol1 filler,
code,
creation "sysdate"
)
INTO TABLE detail_table
WHEN (1) ='D'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' TRAILING NULLCOLS
(col1 filler position(1:2),
pid expression "testseq.currval" ,
name "UPPER(:name)")
BEGINDATA
M,master1
D,master1-detail1
D,master1-detail2
D,master1-detail3
M,master2
D,master2-detail1
M,master3
D,master3-detail1
Без OPTIONS (ROWS = 1) ничего работать не будет -- все дочерние записи привяжутся к последней записи
Решение 2 (натолкнувшее на написание этой задачи)
Допустим у нас есть какой-то признак, по которому мы можем найти родительскую запись (в примере добавил в конце строки еще поле). Будем получать по этому признаку идентификатор родителькой записи:
OPTIONS (ROWS = 1)
LOAD DATA
INFILE *
TRUNCATE
INTO TABLE master_table
WHEN (1) ='M'
FIELDS TERMINATED BY ',' optionally enclosed by '"' TRAILING NULLCOLS
(mcol1 filler,
code,
id,
creation "sysdate"
)
INTO TABLE detail_table
WHEN (1) ='D'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' TRAILING NULLCOLS
(col1 filler position(1:2),
Name "UPPER(:name)",
pid "(select id from master_table WHERE id = :pid)")
BEGINDATA
M,master1,1
D,master1-detail1,1
D,master1-detail2,1
D,master1-detail3,1
M,master2,2
D,master2-detail1,2
M,master3,3
D,master3-detail1,3
Пример так же работает только с OPTIONS (ROWS = 1). Без этого запрос к получению родительского идентификатора выполняется столько раз, сколько COMMIT было сделано и строки привязываются в хаотичном порядке.Но вынесем запрос для получения для идентификатора в функцию:
CREATE OR REPLACE FUNCTION f(aID VARCHAR2) RETURN NUMBER IS
BEGIN
FOR rec IN (select id from master_table WHERE id = aID) LOOP
RETURN rec.id;
END LOOP;
RETURN NULL;
END;
и будем ее вызывать:Load DATA
INFILE *
TRUNCATE
INTO TABLE master_table
WHEN (1) ='M'
FIELDS TERMINATED BY ',' optionally enclosed by '"' TRAILING NULLCOLS
(mcol1 filler,
code,
id,
creation "sysdate"
)
INTO TABLE detail_table
WHEN (1) ='D'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' TRAILING NULLCOLS
(col1 filler position(1:2),
Name "UPPER(:name)",
pid "f(:pid)")
BEGINDATA
M,master1,1
D,master1-detail1,1
D,master1-detail2,1
D,master1-detail3,1
M,master2,2
D,master2-detail1,2
M,master3,3
D,master3-detail1,3
Все работает нормально и без OPTIONS (ROWS = 1).Такое ощущение, что тут что-то аналогичное bind array в dbms_sql -- запросы выполняются 1 раз для первой переменной массива.
Трейс, полученный в результате эксперимента (для немного измененной таблицы) для INSERT:
INSERT INTO BDETAILS (ID,NAME,AMT,FLAG)
VALUES
(f(:"NAME"),(SELECT id FROM master_table WHERE chc = :"NAME") || ' ' ||
UPPER(:"NAME"),:AMT,:FLAG)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 3 0.01 0.00 3 26 26 5
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.01 0.00 3 26 26 5
5 строк вставляются за 3 execute.
Комментариев нет:
Отправить комментарий