у нас есть Oracle 11G
, и я пытаюсь переместить данные из одной таблицы в другую, используя bulk collect
. Проблема в том, что когда я пытался оценить, пусто ли одно поле из источника, мой пакет стал недействительным. Что я имею:
Декларация:
CREATE OR REPLACE PACKAGE MYSCHEMA.MYPKG AS
CURSOR CUR_MYDATA IS
SELECT
o.name,
o.last_name,
o.id,
o.socnum
FROM
origin o
WHERE
1=1
AND o.name like upper ('a%');
TYPE t_name IS TABLE OF origin.name%TYPE;
TYPE t_lastname IS TABLE OF origin.last_name%TYPE;
TYPE t_id IS TABLE OF origin.id%TYPE;
TYPE t_socnum IS TABLE OF origin.socnum%TYPE;
l_name t_name;
l_lastname t_lastname;
l_id t_id;
l_socnum t_socnum;
PROCEDURE MYPROCEDURE;
END MYPKG;
Тело:
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.MYPKG AS
PROCEDURE MYPROCEDURE IS
BEGIN
OPEN CUR_MYDATA;
LOOP
FETCH CUR_MYDATA BULK COLLECT INTO l_name,l_lastname,l_id,l_socnum;
forall i IN 1 .. l_name.COUNT
IF ( l_socnum(i) IS NULL)
THEN (select oo.socnum from other_origin where oo.id=l_id(i))
END IF;
INSERT INTO destiny (
d_name,
d_lastname,
d_id,
d_socnum)
VALUES (
l_name(i),
l_lastname(i),
l_id(i),
l_socnum(i),
EXIT WHEN l_name.count = 0;
END LOOP;
END MYPROCEDURE;
END MYPKG;
но когда я проверяю состояние тела, это INVALID
любые мысли?
Одна очевидная ошибка связана с l_socnum(i),
в конце оператора INSERT
, который следует заменить на l_socnum(i));
@AlexPoole спасибо за советы, я делаю bulk collect
просто потому, что мне так предложили, без особых причин
Всегда хорошо иметь причину что-то делать, особенно когда это что-то может привести к неоптимальной производительности. Это особенно актуально при кодировании массовой операции, когда эффект умножения даже небольшой неэффективности может сильно повлиять на эффективность нашей программы. Есть разница между преждевременной оптимизацией и невыполнением того, что, как мы знаем, негативно скажется на производительности.
@APC, вы правы, на данный момент я не настолько опытен в этой области, чтобы критиковать те варианты, которые у меня есть.
Так что это хорошее время, чтобы приобрести этот опыт. Сравните подход BULK COLLECT с подходом INSERT… SELECT Я вставил свой ответ и посмотрел, что быстрее. Проверка есть начало мудрости.
@APC обязательно сделаю это, как только закончу утренние собрания
Вы можете попробовать следующий код:
Упаковка:
CREATE OR REPLACE PACKAGE MYSCHEMA.MYPKG AS
CURSOR CUR_MYDATA IS
SELECT
O.NAME,
O.LAST_NAME,
O.ID,
-- ADDED THIS CASE STATEMENT
CASE
WHEN O.SOCNUM IS NOT NULL THEN O.SOCNUM
ELSE OO.SOCNUM
END AS SOCNUM
FROM
-- ADDED THIS LEF JOIN
ORIGIN O
LEFT JOIN OTHER_ORIGIN OO ON ( OO.ID = O.ID )
WHERE
1 = 1
AND O.NAME LIKE UPPER('a%');
TYPE T_NAME IS
TABLE OF ORIGIN.NAME%TYPE;
TYPE T_LASTNAME IS
TABLE OF ORIGIN.LAST_NAME%TYPE;
TYPE T_ID IS
TABLE OF ORIGIN.ID%TYPE;
TYPE T_SOCNUM IS
TABLE OF ORIGIN.SOCNUM%TYPE;
L_NAME T_NAME;
L_LASTNAME T_LASTNAME;
L_ID T_ID;
L_SOCNUM T_SOCNUM;
PROCEDURE MYPROCEDURE;
END MYPKG;
Тело пакета
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.MYPKG AS
PROCEDURE MYPROCEDURE IS
BEGIN
OPEN CUR_MYDATA;
FETCH CUR_MYDATA BULK COLLECT INTO
L_NAME,
L_LASTNAME,
L_ID,
L_SOCNUM
LIMIT 1000;
FORALL I IN 1..L_NAME.COUNT
--
-- REMOVED THIS CONDITION
--
-- IF ( l_socnum(i) IS NULL)
-- THEN (select oo.socnum from other_origin where oo.id=l_id(i))
-- END IF;
INSERT INTO DESTINY (
D_NAME,
D_LASTNAME,
D_ID,
D_SOCNUM
) VALUES (
L_NAME(I),
L_LASTNAME(I),
L_ID(I),
L_SOCNUM(I)
);
CLOSE CUR_MYDATA;
END MYPROCEDURE;
END MYPKG;
спасибо за ответ, еще не проверял, но мне кажется неплохой вариант
FORALL не является конструкцией цикла: ее нельзя отделить от инструкции DML.
when I tried to evaluate if one field from origin is empty
Вам нужно зациклиться на заполненной коллекции и исправить это перед выполнением FORALL... INSERT.
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.MYPKG AS
PROCEDURE MYPROCEDURE IS
BEGIN
OPEN CUR_MYDATA;
LOOP
FETCH CUR_MYDATA BULK COLLECT INTO l_name,l_lastname,l_id,l_socnum;
EXIT WHEN l_name.count = 0;
for idx in 1 .. l_socnum.count() loop
IF l_socnum(idx) IS NULL THEN
select oo.socnum
into l_socnum(idx)
from other_origin
where oo.id = l_id(idx);
END IF;
end loop;
forall i IN 1 .. l_name.COUNT
INSERT INTO destiny (
d_name,
d_lastname,
d_id,
d_socnum)
VALUES (
l_name(i),
l_lastname(i),
l_id(i),
l_socnum(i));
END LOOP;
END MYPROCEDURE;
END MYPKG;
Другие примечания.
%rowtype
: это проще, чем определение и обработка нескольких коллекций на основе столбцов.Кроме того, ваш реальный код может быть намного сложнее, чем то, что вы разместили здесь, но если у вас есть большой объем данных для переноса, использование чистого SQL, а не процедуры, дает значительный прирост производительности:
INSERT INTO DESTINY (
D_NAME,
D_LASTNAME,
D_ID,
D_SOCNUM
)
SELECT
o.name,
o.last_name,
o.id,
coalesce(o.socnum, oo.socnum)
FROM
origin o
left outer join other_origin oo
on oo.id = o.id
WHERE
1=1
AND o.name like upper ('a%');
ваш пример сработал для меня, спасибо за него, но вместо этого for idx in 1 .. l_socnum loop
я должен был for idx in 1 .. l_socnum.count loop
Да, извините за это. Всегда трудно обнаружить опечатки в программе, когда у нас нет схемы для компиляции.
Посмотрите на представление
user_errors
, чтобы увидеть, что не так. На первый взгляд у вас естьselect oo.socnum
безinto
; но вы не можете (AFAIK) иметь логику как частьforall
- я думаю, что ваша вставка выходит за рамки этого, что вызовет несколько ошибок. У вас также нетlimit
при массовом сборе, поэтому вы зациклились только один раз. Итак... почему вы не делаете одно простоеinsert ... select
утверждение?