Pl/SQL 拆分表

Posted

技术标签:

【中文标题】Pl/SQL 拆分表【英文标题】:Pl/SQL Splitting a table 【发布时间】:2018-12-16 11:42:24 【问题描述】:

我在 Oracle 中有一张表。我想做几张表,每张表包含这张表的1/10数据(其实我只需要一列)。我能够编写以下代码,但它似乎并不高效,因为它每次都运行整个原始表。

declare
  baseObjid  Number := 100;
  chunkSize  Number;
  totalCount Number;
begin
  select count(1) into totalCount from table_person;
  chunkSize := trunc(totalCount / 10) + 1;
  for i in 1 .. 10 loop
    execute immediate 'create table table_person_' || i ||
                      ' AS (select sel.r + ' || baseObjid ||
                      ' objid,  sel.objid oldId from 
                      (select rownum r, objid from table_person order by objid) sel 
                      where sel.r > ' || (i - 1) * chunkSize || 
                      ' and sel.r <= ' || i * chunkSize || ')';
    commit;
  end loop;
end;

有没有办法让它只访问原始表一次?也欢迎任何其他建议。

【问题讨论】:

我建议改用table partioning。请描述您想要解决的真实问题。 @LukaszSzozda 在没有具体细节的情况下很难描述整个问题。我只能说这些原始表非常大(200 000 000 行),我只需要这些临时表很短的时间。而且 afaik 没有简单的方法可以在不丢失数据的情况下对现有表进行分区。 看起来您仍然需要其他东西。我假设你想要执行:DBMS_PARALLEL_EXECUTE @LukaszSzozda 我会调查 DBMS_PARALLEL_EXECUTE。这几乎是完美的,我只需要弄清楚如何使用它创建具有唯一名称的新表。 @LukaszSzozda - 请记住,在推荐表分区时,分区选项是企业版的额外收费。它不是免费使用的,如果 Oracle 对其进行审计,建议人们在不提示他们检查许可情况的情况下使用它会使他们的组织面临严重的冲击。 【参考方案1】:

从单个查询填充多个表的最简单方法是 INSERT ALL 语句。

此查询通过将模数应用于驱动 select 中的 rownum,将 PERSON 的 ID 拆分到十个预先创建的目标表 PERSON_1 .. PERSON_10。您可以修改查询的投影以给出不同的标准。

insert all
    when rn = 1 then into person_1 (id) values (id) 
    when rn = 2 then into person_2 (id) values (id) 
    when rn = 3 then into person_3 (id) values (id) 
    when rn = 4 then into person_4 (id) values (id) 
    when rn = 5 then into person_5 (id) values (id) 
    when rn = 6 then into person_6 (id) values (id) 
    when rn = 7 then into person_7 (id) values (id) 
    when rn = 8 then into person_8 (id) values (id) 
    when rn = 9 then into person_9 (id) values (id) 
    else into person_10 values (id) 
select id, mod(rownum,10) as rn from person;

或者,您可以通过更改 WHEN 条件来驱动分配,例如到存储桶中:

insert all
    when id < 1000 then into person_1 (id) values (id) 
    when id < 2000 then into person_2 (id) values (id) 
    when id < 3000 then into person_3 (id) values (id) 
    when id < 4000 then into person_4 (id) values (id) 
    when id < 5000 then into person_5 (id) values (id) 
    when id < 6000 then into person_6 (id) values (id) 
    when id < 7000 then into person_7 (id) values (id) 
    when id < 8000 then into person_8 (id) values (id) 
    when id < 9000 then into person_9 (id) values (id) 
    else into person_10 values (id) 
select id from person; 

显然,存储桶可能取决于您发布的代码中的 rownum 列。


值得考虑将表创建与数据填充分开的好处。

在您发布的代码中,如果例程在中途失败(例如无法扩展表空间),您将填充和访问一些表(因为 DDL 发出提交,因此没有回滚)。一旦您修复了错误的来源,您必须在重新运行您的例程之前清理(即删除)这些表。或者您可以更改代码并跳过这些表,但这总是有问题的,尤其是因为在两次运行之间源表可能会发生变化,因此您最终可能会出现不一致的状态。

首先创建空表提供了可恢复的位置。 INSERT ALL 是一条语句,它不仅比十个选择更具执行性,而且意味着要么所有表都已填充,要么没有。

【讨论】:

它工作正常!我很惊讶,因为我一直认为创建表作为选择比插入要快得多。 请注意,如果您尝试通过一次操作进行此插入,您的重做空间将被过度使用。它可能会影响你的表现。 @simonare - 请定义“过度”。记住要考虑事务性和可恢复性。 我希望我能多次对此表示赞同。很好的答案!

以上是关于Pl/SQL 拆分表的主要内容,如果未能解决你的问题,请参考以下文章

使用pl / sql或sql在学生表的多个列中拆分数据

将行拆分为多行 PL/SQL

Oracle PL/SQL 程序在源表中拆分逗号分隔的数据并推送到目标表中

PL/SQL:拆分数字;

Python 正则表达式拆分 PL/SQL 指令

如何在 pl/sql 中将街道值拆分为原子?