【问题标题】:Oracle pl sql 10g - move set of rows from a table to a history table with same structureOracle pl sql 10g - 将一组行从表移动到具有相同结构的历史表
【发布时间】: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 语句完成插入和删除吗?因为要处理大量数据并且跨多个表

【问题讨论】:

    标签: performance oracle plsql oracle10g


    【解决方案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 将选择同一组行。
      • 嗯,这确实解决了我的第一个问题。但是现在,如果作业在处理过程中被中断,它就不能简单地再次安全运行。 (此外,您不知道条件的正确评估是否取决于存在哪些行,因此在多个事务中重复评估该条件可能不会产生所需的结果。)
      • 同意,但我不知道如何改进它以适应您的顾虑
      猜你喜欢
      • 1970-01-01
      • 2014-07-07
      • 2011-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-03
      相关资源
      最近更新 更多