如何在目标中识别附加到目标 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