为什么我基于游标的 table 复制即使在完成该过程后也会完全占用撤消 table 空间?

Why does my cursor based copying of a table leave the undo tablespace completely occupied even after finishing the procedure?

我在一个过程中将大量数据从一个 Table 复制到另一个,同时使用游标迭代一个 table 的数据,将它们保存在一个数组中,然后填充另一个批量有限。


我确实意识到有更好的方法可以做到这一点,但由于语言限制,我采用了这种方式。我的代码:


    PROCEDURE copy_tableA_into_tableB IS
    TYPE tableA_array IS TABLE OF tableA%ROWTYPE;
    tableA_initialized_array TableIwantToCopyFrom_array;  

    CURSOR table_a_cursor IS
        SELECT *
        FROM TABLE_A; --get all data from Table_A

    BEGIN    
    OPEN table_a_cursor;

      LOOP
        FETCH table_a_cursor BULK COLLECT
          INTO test_copy LIMIT 10000;

        FORALL i IN 1 .. table_a_cursor.COUNT
          INSERT INTO TABLE_B
          VALUES tableA_initialized_array (i);
        COMMIT;

        EXIT WHEN table_a_cursor%NOTFOUND;
      END LOOP;

    CLOSE table_a_cursor;
    END copy_tableA_into_tableB;

只要发生一件奇怪的事情,这段代码就可以正常工作, 当我用像 150 万 table 行这样的大量数据执行它几次时,UNDO tablespace 变得越来越大,即使程序完成了它仍然由分配我的程序。最终我的 UNDO tablespace 已满,我得到一个异常。事实上,我只能放弃我的 UNDO tablespace 并重建它以使其空置并再次清空。

你可以清楚地看到我每次通过数组时都在提交,所以为什么在事务完成后 UNDO table space 仍然被分配?


我不是理解底层概念的 Oracle 专家,但我认为我的游标已关闭并在他关闭时重新分配,所以我不认为他是罪魁祸首,如果这是任何问题。

我预计当我完成该过程时,我的 UNDO table space 会再次被释放,我正在检查仍然存在的 undo table space保持不变

编辑未回答的问题:

我正在检查有多少 UNDO tablespace 我留下了数据的数量加起来,我是唯一的 运行 程序,

异常:ORA-01555:快照太旧:名称为“”的回滚段号太小

我正在 PL/SQL 开发人员中测试存储过程测试中的过程 我不会重置任何东西,只是清空我想用截断复制到的 tables。

Oracle 不会重用 space 那里有一个活动事务。 这就解释了为什么你的 tablespace 完全被占用了

更多信息在此 Undo tablespace keeps growing

如果你想知道space占用了多少

select tablespace_name,sum(bytes) from dba_segments group by tablespace_name;

这样你就可以找到实际尺寸

select tablespace_name,sum(bytes) from dba_data_files group by tablespace_name;

首先,我不明白"language restrictions"是什么意思。为什么这会阻止您进行直插入?

其次,您正在循环提交 - 说真的,这是一个非常非常糟糕的主意。您可能会发现自己收到快照太旧的错误。

第三,对于你的回答,Oracle 不会立即在 UNDO 中释放 space,一旦你完成它 - 它被标记为不再需要(你对该提交所做的事情,因此为什么跨循环获取是个坏主意!)如果另一个会话需要它 space 那么它会被覆盖。

Tom Kyte,一如既往,says it best

不就是因为开场白是:

光标 table_a_cursor 是 SELECT * 来自 TABLE_A; --从Table_A

获取所有数据

因此生成undo,以便本次查询能够成功。这些行上的锁直到完成后才会释放(此时 UNDO 表空间已满)。

这样的事情应该可行:(可能需要对循环进行一些修改 - 我个人会检查提交前处理的 'x' 而不是此方法。

PROCEDURE Copy_tableA_into_tableB AS
    l_count integer
         BEGIN
         -- Count rows in tableA
        l_count_total := 'select count(*) from table A';
             EXECUTE IMMEDIATE l_sql_regexp_count
             INTO l_pancount;
    WHILE l_count > 0
    LOOP
        FETCH table_a_cursor BULK COLLECT
        INTO cdplzstb_copy_batch LIMIT 10000;
        FORALL i IN 1 .. table_a_cursor.COUNT
        INSERT INTO TABLE_B
        VALUES tableA_initialized_array (i);
    COMMIT;
    l_count:=l_count-1;
    END LOOP;

END Copy_tableA_into_tableB;