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

Oracle 为什么即使在完成该过程后,基于游标的表复制仍会完全占用撤消表空间?,oracle,stored-procedures,plsql,oracle11g,Oracle,Stored Procedures,Plsql,Oracle11g,我在一个过程中将大量数据从一个表复制到另一个表,同时使用游标对一个表的数据进行迭代,将它们保存在一个数组中,然后在有限的批大小中填充另一个表 我确实意识到有更好的方法可以做到这一点,但我之所以这样做是因为语言的限制。我的代码: 只要发生一件奇怪的事情,这段代码就可以正常工作, 当我使用大量数据(如150万个表行)执行它几次时,UNDO表空间变得越来越大,即使过程完成了,它仍然由我的过程分配。最终我的撤销表空间满了,我得到了一个异常。事实上,我只能删除我的UNDO表空间并重新构建它,以使其空

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


我确实意识到有更好的方法可以做到这一点,但我之所以这样做是因为语言的限制。我的代码:



只要发生一件奇怪的事情,这段代码就可以正常工作, 当我使用大量数据(如150万个表行)执行它几次时,UNDO表空间变得越来越大,即使过程完成了,它仍然由我的过程分配。最终我的撤销表空间满了,我得到了一个异常。事实上,我只能删除我的UNDO表空间并重新构建它,以使其空闲并再次清空

正如您可以清楚地看到的,实际上每次通过数组时我都在提交,那么为什么在事务完成后仍然会分配撤消表空间呢?
我不是oracle专家,无法理解其基本概念,但我认为我的游标在关闭时已关闭并释放,因此我不认为他是罪魁祸首,如果有任何问题,我使用的是oracle版本11g

我希望当我完成这个过程时,我的撤销表空间会再次被释放,并且我会检查仍然保持不变的撤销表空间

编辑未回答的问题: 我正在检查我留下了多少UNDO表空间,要占用的数据数量加起来了,我是唯一一个运行程序的人

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

我正在PL/SQL开发人员的存储过程测试中测试该过程
我没有重置任何内容,只是清空了要复制到的表,并进行了截断。

Oracle不会在存在活动事务时重用空间。 这就解释了为什么表空间被完全占用了

更多信息在此

如果你想知道占用了多少空间

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;

首先,我不明白你说的“语言限制”是什么意思。为什么这会阻止你直接插入

第二,你在做一个循环——说真的,一个坏主意。您可能会发现自己得到的快照太旧了

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


Tom Kyte一如既往地说,这不是因为开场白是:

光标表\u\u光标为 挑选* 从表A--从表A获取所有数据

因此,将生成撤消,以便此查询能够成功。这些行上的锁直到完成后才释放(此时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;

您正在运行哪些查询以查看分配给您的过程的空间?您会遇到什么异常?当您“执行几次”时,您是否在每次运行之前重置了某些内容(例如可能在不清除的情况下删除和重建目标表)?以及您的闪回设置是什么?它会注意到,如果存在事务,请重新使用空间需要等待一些timeLanguage限制:数据模型没有更改,x数据集提交后也没有更改。我实际上应该添加这三个注释,但事务肯定已经结束。快照不应该太旧,因为除了我之外没有人使用表。提取部分没有完全启发我,老实说,当它完成后,程序退出了过程本地占用的空间应该被释放,对吗?显然,我可以理解,当我在过程中时,撤消表空间很高,我只是在完成事务后说。如果你没有得到快照太旧的错误,你是幸运的。就是这样。一旦你发出第一个com麻省理工学院,你告诉Oracle,你不再需要任何最终存储在UNDO中的数据。这意味着任何其他会话都可以自由覆盖该数据,并且,如果你的会话稍后需要访问被覆盖的信息,那么wham-snapshot太旧了。有关更多信息,请参阅。至于UNDO表空间的整理,我不是完整的我很确定这是怎么回事——我猜所有的空白空间都会先被填满,然后Oracle会四处搜索不再活动的块并覆盖它们。也可能会有一个定期的工作来清除任何不再活动的块,但我不确定。“在x个数据集之后,提交”-如果您必须对结果进行批处理(认真地说,为什么?),那么最好采用某种方式标记要插入到块中的行,然后直接插入(例如,
插入blahblah select…from(选择row_number()over(order by rn,….from some_table),其中rn>=:start_number和rn<:end_number;
),但这比仅仅插入要慢:
insert into blablah select…from some_table;
在这里是我认为最有效的信息,所以我宣布它是正确的Answare,不管怎样,每个Answare都是有用的。因此,从您发布的链接中,我了解到撤销不会返回到0使用,因为它与uld重用从事务中分配的空间。但是,在我的示例中,在事务完成后,我开始另一次运行,而不是重用他的空间,为什么要在他满之前添加更多空间?因为插入空sp更容易/更快
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;