Как можно идентифицировать в целевой таблице Oracle записи данных, добавленные к целевой таблице Oracle, которые впоследствии были удалены из ее источника данных?

#oracle #stored-procedures

#Oracle #хранимые процедуры

Вопрос:

Периодически таблица Oracle, называемая ED, обновляется с помощью хранимой процедуры. Источник данных представляет собой сложную инструкцию INSERT-SELECT, и ED усекается каждый раз, когда происходит обновление. Однако, прежде чем произойдет усечение, желательно знать, не будут ли какие-либо записи, существующие в ED, повторяться при обновлении, т.Е. Выбор ВСТАВКИ не будет повторно вводить определенные записи, которые в настоящее время существуют в ED.

Идея решения этой проблемы состоит в том, чтобы сначала выполнить INSERT_SELECT во временную таблицу с именем ED_TEMP . А затем сравните ED с ED_TEMP и поместите удаленные записи в другую таблицу с именем ED_MISSING .

 INSERT INTO ED_MISSING (ED_ID, ENTRY_DATE)
    SELECT DISTINCT ED_ID, ENTRY_DATE FROM ED
    WHERE ED_ID NOT IN (SELECT ED_ID FROM ED_TEMP); 
 

После этого желательно обрезать ED и повторно заполнить его с помощью ED_TEMP . Можно ли все это сделать в рамках хранимой процедуры? Или было бы лучше использовать несколько хранимых процедур?

 1. Truncate ED_TEMP.
2. Perform complex INSERT-SELECT to populate ED_TEMP.
3. Handle exception if necessary
4. Perform compare and populate ED_MISSING
5. Handle exception if necessary.
6. Truncate ED.
7. Populate ED with ED_TEMP.

Would separate COMMIT and EXCEPTION statements be necessary for each INSERT step? 
 

Ответ №1:

Можно ли все это сделать в рамках хранимой процедуры?

Конечно; вам просто нужно будет обрезать таблицы, используя execute immediate как truncate есть DDL. Также обратите внимание, что DDL неявно фиксирует, дважды — до и после фактического оператора DDL.


Или было бы лучше использовать несколько хранимых процедур?

Не могу сказать; если весь процесс выполняет одну транзакцию, сохраните ее в той же процедуре. Или, если он действительно сложный, его легче поддерживать, если он разбит на более мелкие части. Некоторые люди говорят, что идеальный размер процедуры — это одна высота экрана (чтобы вы могли видеть все сразу). Однако я не думаю, что это всегда возможно.


Будут ли необходимы отдельные операторы ФИКСАЦИИ и ИСКЛЮЧЕНИЯ для каждого шага вставки?

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

Что касается исключений, то вы знаете лучше всех. Если есть, например, 2 select инструкции, у вас может быть один обработчик исключений для no_data_found . Но он будет обрабатывать ОБА выбора; как вы узнаете, какой из них не удался? В этом случае инкапсулируйте каждый из них в свой собственный BEGIN-EXCEPTION-END блок.

Комментарии:

1. У меня нет большого опыта в этом деле. Могу ли я иметь несколько блоков BEGIN-EXCEPTION-END в одной хранимой процедуре? И если я выполняю ВСТАВКУ в ED_TEMP, мне не нужно фиксировать ее, чтобы иметь возможность сравнивать ее с таблицей ED, верно?

2. У вас может быть столько блоков BEGIN-EXCEPTION-END в пределах одной процедуры (т. Е. «Самого внешнего» BEGIN-END), сколько вы хотите, без проблем. Да, вы правы — вам не нужно фиксировать использование данных в таблице ED. Любые изменения, которые вы вносите в свой собственный сеанс, всегда видны вам (но никому другому, пока вы не ЗАФИКСИРУЕТЕ).

Ответ №2:

С помощью ответа Литтлфута я решил проблему следующим образом.

Создайте хранимую процедуру (ED_MAIN), которая вызывает две другие хранимые процедуры. Первая хранимая процедура (POPULATE_ED_TMP) усекает, а затем повторно заполняет временную таблицу ED_TMP. Затем вторая хранимая процедура выполняет вставку в таблицу ED_MISSING.

 BEGIN
INSERT INTO ED_MISSING(column1,...) --a primary key or unique index
SELECT column1,... FROM
(
SELECT column1,... FROM ED  -- the previous data set in the ED table
MINUS
SELECT column1,... FROM ED_TMP --the current data set
)
MINUS
SELECT column1,... FROM ED_MISSING --don't put duplicates in ED_MISSING
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE_APPLICATION_ERROR(-20002, 'Insert into GATED_MISSING error - '||    SQLCODE||' - '||SQLERRM); 
END;
 

Наконец, в ED_MAIN переместите содержимое ED_TMP в ED в prep для следующего сравнения

 EXECUTE IMMEDIATE 'TRUNCATE TABLE ED'
INSERT INTO ED(column1, column2, column3, column4,...)
SELECT column1, column2, column3, column4,... FROM ED_TMP