如何在目标中识别附加到目标 Oracle table 且随后从其数据源中删除的数据记录?
How can data records appended to a target Oracle table that have subsequently been removed from its data source be identified in the target?
一个名为 ED 的 Oracle table 会定期通过存储过程进行更新。数据源是一个复杂的 INSERT-SELECT 语句,每次更新时 ED 都会被截断。但是,在截断发生之前,需要知道 ED 中存在的任何记录是否不会在更新时重新出现,即 INSERT-SELECT 不会重新引入 ED 中当前存在的某些记录。
解决这个问题的思路是先把INSERT_SELECT执行成一个叫ED_TEMP的临时table。然后将 ED 与 ED_TEMP 进行比较,并将删除的记录放在另一个名为 ED_MISSING.
的 table 中
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?
Can this all be done within a stored procedure?
当然;您只需要使用 execute immediate
截断表,因为 truncate
是 DDL。另外,请注意 DDL 隐式提交两次 - 在实际 DDL 语句之前和之后。
Or would multiple stored procedures be better?
看不出来;如果整个过程进行一笔交易,则将其保留在同一程序中。或者,如果它真的很复杂,将它拆分成更小的部分会更容易维护。有人说理想的程序大小是一个屏幕的高度(这样你就可以一下子看到所有的东西)。不过,我不认为这 总是 可能。
Would separate COMMIT and EXCEPTION statements be necessary for each INSERT step?
视情况而定; commit 结束事务 - 如果之后出现问题,则不会回滚。因此,如果你所做的一切都被认为是同一个事务,那么提交一次——在最后。但是,如果您处理大量数据,回滚段可能无法 吞下 所有数据,因此您可能需要在此过程中提交。
至于例外情况,您最了解。如果有例如2 select
条语句,您可以为 no_data_found
设置一个异常处理程序。但是,它会处理两个选择;你怎么知道哪个失败了?在那种情况下,将它们中的每一个封装到它自己的 BEGIN-EXCEPTION-END
块中。
在 Littlefoot 的回答的帮助下,我通过以下方式解决了问题。
创建调用另外两个存储过程的存储过程 (ED_MAIN)。第一个存储过程 (POPULATE_ED_TMP) 截断然后重新填充临时 table ED_TMP。第二个存储过程然后插入到 ED_MISSING table.
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 以准备下一次比较
EXECUTE IMMEDIATE 'TRUNCATE TABLE ED'
INSERT INTO ED(column1, column2, column3, column4,...)
SELECT column1, column2, column3, column4,... FROM ED_TMP
一个名为 ED 的 Oracle table 会定期通过存储过程进行更新。数据源是一个复杂的 INSERT-SELECT 语句,每次更新时 ED 都会被截断。但是,在截断发生之前,需要知道 ED 中存在的任何记录是否不会在更新时重新出现,即 INSERT-SELECT 不会重新引入 ED 中当前存在的某些记录。
解决这个问题的思路是先把INSERT_SELECT执行成一个叫ED_TEMP的临时table。然后将 ED 与 ED_TEMP 进行比较,并将删除的记录放在另一个名为 ED_MISSING.
的 table 中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?
Can this all be done within a stored procedure?
当然;您只需要使用 execute immediate
截断表,因为 truncate
是 DDL。另外,请注意 DDL 隐式提交两次 - 在实际 DDL 语句之前和之后。
Or would multiple stored procedures be better?
看不出来;如果整个过程进行一笔交易,则将其保留在同一程序中。或者,如果它真的很复杂,将它拆分成更小的部分会更容易维护。有人说理想的程序大小是一个屏幕的高度(这样你就可以一下子看到所有的东西)。不过,我不认为这 总是 可能。
Would separate COMMIT and EXCEPTION statements be necessary for each INSERT step?
视情况而定; commit 结束事务 - 如果之后出现问题,则不会回滚。因此,如果你所做的一切都被认为是同一个事务,那么提交一次——在最后。但是,如果您处理大量数据,回滚段可能无法 吞下 所有数据,因此您可能需要在此过程中提交。
至于例外情况,您最了解。如果有例如2 select
条语句,您可以为 no_data_found
设置一个异常处理程序。但是,它会处理两个选择;你怎么知道哪个失败了?在那种情况下,将它们中的每一个封装到它自己的 BEGIN-EXCEPTION-END
块中。
在 Littlefoot 的回答的帮助下,我通过以下方式解决了问题。
创建调用另外两个存储过程的存储过程 (ED_MAIN)。第一个存储过程 (POPULATE_ED_TMP) 截断然后重新填充临时 table ED_TMP。第二个存储过程然后插入到 ED_MISSING table.
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 以准备下一次比较
EXECUTE IMMEDIATE 'TRUNCATE TABLE ED'
INSERT INTO ED(column1, column2, column3, column4,...)
SELECT column1, column2, column3, column4,... FROM ED_TMP