使用'connect by'重复行已运行很长时间

时间:2014-08-01 18:35:26

标签: sql oracle

我想根据父表中的开始日期和结束日期插入多个行。我尝试了以下查询,但它运行了一个小时。

INSERT INTO CHILD_TABLE
  with num as (
    select level as rnk 
     from dual 
  connect by level<=300
  ), 
  Select Data, start_date,
         end_date,rnk
    From Paratent_table
    Join num 
      ON (num.rnk <= end_date-start_date)

父表的行数超过一百万。

1 个答案:

答案 0 :(得分:1)

使用CARDINALITY提示,直接路径插入,并行性和删除公用表表达式,可以将问题的通用版本从6分钟提高到22秒。可能还有一些其他因素可以解释为什么您的版本在60分钟内运行并且我的版本在6分钟内运行。如果在进行这些更改后仍然存在问题,请使用SQL监视来找出问题所在的操作和等待事件。

示例架构

--drop table child_table;
--drop table parent_table;

create table parent_table(data varchar2(100), start_date date, end_date date);
create table child_table (data varchar2(100), start_date date, end_date date, rank number);

--Insert 1 million rows, gather statistics.
begin
    for i in 1 .. 10 loop
        insert into parent_table
        select level, sysdate,sysdate+50
        from dual
          connect by level <= 100000;
    end loop;
end;
/
begin
    dbms_stats.gather_table_stats(user, 'parent_table');
end;
/

原始版本 - 在旧桌面上运行6-10分钟

insert into child_table
with num as
(
    select level as rnk 
    from dual 
    connect by level<=300
)
select data, start_date, end_date, rnk
from parent_table
join num 
    on num.rnk <= end_date-start_date;

commit;
truncate table child_table;

新版本 - 在22秒内运行

insert /*+ append parallel */ into child_table
select data, start_date, end_date, rnk
from parent_table
join
(
    select /*+ cardinality(300) */ level as rnk 
    from dual 
    connect by level<=300
) num
    on num.rnk <= end_date-start_date;

更改说明

CARDINALITY提示通知优化器内联视图返回300行,而原始估计值为1行。改进的基数估计将计划从NESTED LOOP更改为MERGE JOIN。

直接路径插入可避免大多数REDO生成。虽然使用简单的样本表,但语句的INSERT部分仍然很昂贵。如果有许多索引或外键需要验证,那么您的真实示例可能会在INSERT中花费大量时间。请注意,如果没有REDO,表格更改不会自动备份。

并行性利用多种资源,可以成为真正的游戏改变者。并行性确实给系统带来了更大的压力,并且可能非常“不公平”。到其他过程。它需要企业版,合理的配置等。

公用表表达式(CTE)适用于重复的查询块。 CTE有时会导致性能问题,但在这种情况下不会。这更像是一种风格问题;内联视图比CTE更容易调试,但这里很难解释。

最后,如果上述方法都不起作用,则需要增加分析的粒度。大多数调优方法只关注哪些SQL语句很慢,然后猜测该SQL语句中的哪些操作是慢的。没有必要猜测,实时SQL监控将告诉您每个操作需要多长时间以及它等待的时间。找到SQL_ID并运行如下语句:select dbms_sqltune.report_sql_monitor(sql_id => '13gdzd4w5fx4y', type => 'text') from dual;