Oracle原子存储过程

时间:2013-04-15 16:18:04

标签: oracle stored-procedures

我正在编写Oracle中的存储过程,它将刷新包含非规范化数据的表。该程序的大纲是:

CREATE OR REPLACE PROCEDURE loadDenormalizedTable IS
BEGIN

DELETE FROM denormalizedTable;

INSERT INTO
    denormalizedTable
    (
        data
    )
SELECT DISTINCT
    data
FROM
    normalizedTables;

END;
/

我希望所有这些都发生在一个事务中,以便表中始终有数据。现在,删除运行并且表空为几分钟,直到插入完成。在没有任何停机时间的情况下处理此类表刷新的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

如果Oracle分区选项可用,那么最好也是最有效的方法是使用分区交换操作。这是在许多大型数据仓库中完成的非常常见的操作。

请参阅以下示例:

http://www.akadia.com/services/ora_exchange_partition.html http://gerardnico.com/wiki/database/oracle/partition_exchange_loading

例如,您可以想象第二个表具有与denormalizedTable相同的精确结构。要实现加载操作:

  • truncate denormalizedTable2
  • 将您的数据插入denormalizeTable2
  • 交换表denormalizedTable和denormalizedTable2
  • 的分区

它通常可以保证对正在运行的应用程序产生最小的影响。

答案 1 :(得分:1)

默认情况下,过程将作为会话拥有的较大事务的一部分执行。正如documentation提到:

  

注意:事务可以跨多个块,一个块可以包含多个事务。

使用您列出的代码,在调用过程后提交之前,其他任何会话都不会看到您的删除插入。如果您只是从SQL * Plus提示符执行它,例如:

SQL> exec loadDenormalizedTable;

PL/SQL procedure successfully completed.

SQL>

...然后,即使在删除和插入完成后,查看该表的任何其他人仍将看到旧数据。 (试图执行该过程,或在denormalizedTable中插入或删除数据的任何其他人都将阻止,但可能您只是期望其他人查询它)。一旦你发出commit,那么每个人都会看到同样的事情。

获得您描述的行为的唯一方法是在过程中手动结束事务:

CREATE OR REPLACE PROCEDURE loadDenormalizedTable IS
BEGIN

DELETE FROM denormalizedTable;

COMMIT WORK; -- makes the delete visible elsewhere

INSERT INTO
    denormalizedTable
    (
        data
    )
SELECT DISTINCT
    data
FROM
    normalizedTables;    
END;
/

在程序中间你不需要commit,并且你很可能或者应该想要这样做,因为它打破了原子性。

你有可能在没有意识到的情况下做一些隐式提交;也许调用另一个做自己提交的过程或函数(不这样做的原因之一 - 它可能有意想不到的副作用!),或者可能是一个DDL语句 - 它总是会在幕后进行隐式提交,但是你无论如何,必须使用动态SQL做到这一点。

另一种可能性是你实际上并没有delete,而是你正在做truncate。如in the documentation暗示的那样,如果没有明确的提交,那么其他所有人都可以立即看到这一点。这也与你提供的大纲有很大的不同。