Oracle pl sql 10g - 将一组行从表移动到具有相同结构的历史表

Posted

技术标签:

【中文标题】Oracle pl sql 10g - 将一组行从表移动到具有相同结构的历史表【英文标题】:Oracle pl sql 10g - move set of rows from a table to a history table with same structure 【发布时间】:2011-08-19 19:04:55 【问题描述】:

PL SQL 将旧版本的数据从事务表移动到具有相同结构的历史表并存档一段时间 -

for each record
insert into tab_hist (select older_versions of current row);
delete from tab (select older_versions of current row);
END

ps:之前我们没有归档(没有插入)——但是在添加插入之后,它的运行时间增加了一倍——那么我们可以使用单个 select 语句完成插入和删除吗?因为要处理大量数据并且跨多个表

【问题讨论】:

【参考方案1】:

这是批处理操作,对吗?在这种情况下,您应该避免逐行并使用集合处理。 SQL 是关于集合的乐趣。

Oracle 具有出色的批量 SQL 处理能力。您发布的伪代码如下所示:

declare
    cursor c_oldrecs is
        select * from your_table
        where criterion between some_date and some_other_date;
    type rec_nt is table of your_table%rowtype;
    oldrecs_coll rec_nt;    
begin
     open c_oldrecs;
     loop
         fetch c_oldrecs into oldrecs_coll limit 1000;
         exit when oldrecs_coll.count() = 0;

         forall i in oldrecs_coll.first() oldrecs_coll.last()
             insert into your_table_hist
             values oldrecs_coll(i);
         forall i in oldrecs_coll.first() oldrecs_coll.last()
             delete from your_table
             where pk_col = oldrecs_coll(i).pk_col;
     end loop;
end;
/   

这种批量处理速度更快,因为它一次向数据库发送一千条语句,而不是在 PL/SQL 和 SQL 之间切换一千次。 LIMIT 1000 条款的存在是为了防止一个非常大的选择影响 PGA。在您的情况下,这种保护措施可能不是必需的,或者您可以使用更高的价值。


我认为您当前的实现是错误的。最好只将当前版本保留在 live 表中,并将所有历史版本保留在单独的表中。使用触发器将历史记录作为每个事务的一部分。

【讨论】:

那个 fetch 上应该有bulk collect 吗? @APC 谢谢 - 我为当前行选择的 oldrecs 一次将在 5 到 30 之间 - 所以限制不会发挥作用【参考方案2】:

您看到的缓慢可能是由于选择要移动哪些行的逻辑所致。如果是这样,您可能会通过执行一次选择以将 rowids 放入内存中的嵌套表中,然后根据该列表执行插入和删除,从而获得更好的结果;或者,使用选择要移动的行的查询来驱动循环。

您可能会考虑在插入时创建一个触发器,该触发器将移动与正在插入的行“匹配”的现有行。这会稍微减慢插入速度,但意味着您不需要任何过程来批量移动旧行。

【讨论】:

是的,由于选择 - 将与第一个一起使用 - 类似于 @APC 的建议【参考方案3】:

如果您使用的是带有分区选项的企业版,请查看分区交换。

【讨论】:

【参考方案4】:

就这么简单

CREATE BACKUP_TAB AS SELECT * FROM TAB

如果您要删除很多行,您将访问您的撤消表空间,并且删除 100k 行的删除可能会导致性能问题。您最好一次批量删除 5k 行并提交。

BEGIN
-- Where condition on insert and delete must be the same
loop
  INSERT INTO BACKUP_TAB SELECT * FROM TAB WHERE 1=1 and rownum < 5000; --Your condition here
  exit when SQL%rowcount < 4999;
  commit;
end loop;
loop
  DELETE FROM TAB
   where 1=1--Your condition here
     and rownum < 5000;
  exit when SQL%rowcount < 4999;
  commit;
end loop;
commit;
END;

【讨论】:

那个示例代码对我来说似乎是个坏主意。不能保证 INSERT 和 DELETE 将选择同一组行。 嗯,这确实解决了我的第一个问题。但是现在,如果作业在处理过程中被中断,它就不能简单地再次安全运行。 (此外,您不知道条件的正确评估是否取决于存在哪些行,因此在多个事务中重复评估该条件可能不会产生所需的结果。) 同意,但我不知道如何改进它以适应您的顾虑

以上是关于Oracle pl sql 10g - 将一组行从表移动到具有相同结构的历史表的主要内容,如果未能解决你的问题,请参考以下文章

Oracle PL/SQL:将整行从触发器转发到过程

oracle 10g pl/sql 有条件的循环选择

PL/SQL Oracle 10g 询问用户输入(替代变量除外)

使用 PL/SQL 过程在 oracle 10g 中转储表

如何在 java 中使用 jdbc 为 oracle 10g 执行 log miner PL/SQL 查询

如何从 Oracle 10G PL/SQL 函数和过程中查找所有表引用? [复制]