2

我正在编写一个在 Oracle 中将刷新包含非规范化数据的表的存储过程。该程序的概要是:

CREATE OR REPLACE PROCEDURE loadDenormalizedTable IS
BEGIN

DELETE FROM denormalizedTable;

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

END;
/

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

4

2 回答 2

2

By default, a procedure will execute as part of a larger transaction owned by the session. As the documentation mentions:

Note: A transaction can span multiple blocks, and a block can contain multiple transactions.

With your code as outlined, no other session will see your delete or insert until you commit after calling the procedure. If you just execute it from an SQL*Plus prompt for example:

SQL> exec loadDenormalizedTable;

PL/SQL procedure successfully completed.

SQL>

... then anybody else looking at the table will still see the old data, even after both the delete and insert have completed. (Anyone else trying to execute the procedure, or insert or delete data in denormalizedTable, will block, but presumably you're only expecting others to be querying it). Once you issue a commit then everyone will see the same thing.

The only way to get the behaviour you describe is to manually end the transaction within the procedure:

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;
/

You do not not need to commit in the middle of the procedure, and it's very rare that you would or should ever want to do that as it breaks the atomicity.

It's possible you're doing something that does an implicit commit without you realising it; maybe calling another procedure or function that does its own commit (one of the reasons not to do that - it can have unexpected side-effects!), or perhaps a DDL statement - which will always do an implicit commit behind the scenes, but you'd have to be doing that with dynamic SQL anyway.

One other possibility is that you aren't actually doing a delete, but that you are doing a truncate. That would be visible to everyone else immediately, without an explicit commit, as hinted at in the documentation. That would also be a significant departure from the outline you provided though.

于 2013-04-15T16:56:14.703 回答
1

If Oracle partitioning option is available, the best, and most efficient way to do it is to use the partition swapping operation. This is a very common operation done in many massive data warehouses.

See some examples at:

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

For instance you could imagine having a second table with the same exact structure as denormalizedTable. To implement your load operation:

  • truncate denormalizedTable2
  • insert your data into denormalizeTable2
  • exchange the partition of tables denormalizedTable and denormalizedTable2

It usually guarantees a minimal impact on the running application.

于 2013-04-15T16:55:28.797 回答