Как выполнить условную обработку в цикле массового сбора?

у нас есть 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

любые мысли?

Посмотрите на представление user_errors, чтобы увидеть, что не так. На первый взгляд у вас есть select oo.socnum без into; но вы не можете (AFAIK) иметь логику как часть forall - я думаю, что ваша вставка выходит за рамки этого, что вызовет несколько ошибок. У вас также нет limit при массовом сборе, поэтому вы зациклились только один раз. Итак... почему вы не делаете одно простое insert ... select утверждение?

Alex Poole 28.05.2019 01:09

Одна очевидная ошибка связана с l_socnum(i), в конце оператора INSERT, который следует заменить на l_socnum(i));

Barbaros Özhan 28.05.2019 04:00

@AlexPoole спасибо за советы, я делаю bulk collect просто потому, что мне так предложили, без особых причин

downtheroad 28.05.2019 14:51

Всегда хорошо иметь причину что-то делать, особенно когда это что-то может привести к неоптимальной производительности. Это особенно актуально при кодировании массовой операции, когда эффект умножения даже небольшой неэффективности может сильно повлиять на эффективность нашей программы. Есть разница между преждевременной оптимизацией и невыполнением того, что, как мы знаем, негативно скажется на производительности.

APC 28.05.2019 15:18

@APC, вы правы, на данный момент я не настолько опытен в этой области, чтобы критиковать те варианты, которые у меня есть.

downtheroad 28.05.2019 15:25

Так что это хорошее время, чтобы приобрести этот опыт. Сравните подход BULK COLLECT с подходом INSERT… SELECT Я вставил свой ответ и посмотрел, что быстрее. Проверка есть начало мудрости.

APC 28.05.2019 15:29

@APC обязательно сделаю это, как только закончу утренние собрания

downtheroad 28.05.2019 15:58
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
833
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

  • Условие ЕСЛИ не допускается внутри FOR ALL.
  • FOR ALL может выполнять один оператор DML: INSERT, UPDATE или DELETE, который записывается после него. Это не нормально для цикла.

Вы можете попробовать следующий код:

Упаковка:

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;

спасибо за ответ, еще не проверял, но мне кажется неплохой вариант

downtheroad 28.05.2019 21:07
Ответ принят как подходящий

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; 

Другие примечания.

  1. Проверьте, возвращает ли выборка какие-либо записи сразу после выполнения выборки. В противном случае ваш код попытается выполнить код над пустой коллекцией, что завершится ошибкой.
  2. Вы должны определить коллекцию на основе целевой таблицы %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

downtheroad 28.05.2019 21:06

Да, извините за это. Всегда трудно обнаружить опечатки в программе, когда у нас нет схемы для компиляции.

APC 28.05.2019 21:33

Другие вопросы по теме