2015-10-20 74 views
4

我们在Oracle数据库中为一个表(已经有20亿行)建立了一个删除查询。该查询是作为PL/SQL Proc的一部分执行的。以下是我们目前仍在测试中的查询。Oracle查询优化一个棘手的删除查询

DELETE from TABLE1 
    where ROWID IN (SELECT rid from (SELECT ROWID rid, ROW_NUMBER() over (PARTITION BY C1_Varchar2,C2_Varchar2 ORDER BY C3_Date desc) as Rank 
           from TABLE1 where C3_Date < ADD_MONTHS(SYSDATE, -20)) 
       where Rank <> 1); 

该查询删除所有来自当月较旧的20个月,除了由C1和C2列的独特组合而形成的最新记录的记录(从表1)。使用此查询将删除大约12%的记录。

当我们运行查询时,我们得到下面的错误。在递归SQL水平发生错误2 ORA-04031::

ORA-00604无法分配32个字节的共享存储器( “共享库” 中,“选择i.obj#,#i.ts,我。文件#,...“,”SQLA“,”tmp“)

请注意,该表是基于C3_Date列进行分区的。但是通过上面的逻辑,我们将在分区中保留很少的记录,因此无法选择删除整个分区。

任何人都可以建议如何解决这个删除它更有效和稳定?

计划如下:

Plan hash value: 2112788339 

--------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation     | Name    | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | Pstart| Pstop | 
--------------------------------------------------------------------------------------------------------------------------- 
| 0 | DELETE STATEMENT    |     |  1 | 59 |  | 9080K (2)| 30:16:07 |  |  | 
| 1 | DELETE      | TABLE1    |  |  |  |   |   |  |  | 
| 2 | NESTED LOOPS    |     |  1 | 59 |  | 9080K (2)| 30:16:07 |  |  | 
| 3 | VIEW      | VW_NSO_1   | 496M| 5684M|  | 6785K (1)| 22:37:12 |  |  | 
| 4 |  SORT UNIQUE    |     |  1 | 11G|  |   |   |  |  | 
|* 5 |  VIEW     |     | 496M| 11G|  | 6785K (1)| 22:37:12 |  |  | 
| 6 |  WINDOW SORT   |     | 496M| 20G| 26G| 6785K (1)| 22:37:12 |  |  | 
|* 7 |  INDEX SKIP SCAN  | XPKTABLE1   | 496M| 20G|  | 1206K (1)| 04:01:18 |  |  | 
| 8 | TABLE ACCESS BY USER ROWID| TABLE1    |  1 | 47 |  |  1 (0)| 00:00:01 | ROWID | ROWID | 
--------------------------------------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    5 - filter("RANK"<>1) 
    7 - access("C3_Date"<ADD_MONTHS([email protected]!,-15)) 
     filter("C3_Date"<ADD_MONTHS([email protected]!,-15)) 
+0

您可以发布此查询的解释计划吗?只需执行'EXPLAIN PLAN FOR DELETE TABLE1 where .... .....',然后运行'SELECT * FROM Table(DBMS_XPLAN.Display)',然后复制结果(作为文本),并将其粘贴到这个问题。 – krokodilko

+0

4031是一个不寻常的错误,可能与查询优化无关。 4031意味着重大的内存问题。这些问题可能是由其他进程引起的,而失败的查询并不是问题的真正原因。如果它只是一个测试数据库,并且你认为这是一种侥幸,那么我只是重新启动它。 –

回答

0

此查询产生大量作为SQL计划和查询估计运行30小时以上所示温度段(26G)的。因此您在运行时会收到ORA-04031。

这些是我的建议。

我建议你不要在大段的子查询中使用分析函数,因为它们会阻止优化器从子查询中解开并因此生成运行时视图。

第二个建议是追加分区消除谓词,并通过使用文字或绑定变量显式提供日期值。在执行查询之前,在PL/SQL块中预先计算它。不要使用非确定性函数SYSDATE作为条件。此外,保留较低日期限制的合理条件(这是设计的关注点)将会很好。

第三种方法是让Oracle自己查找必须通过使用另一个条件删除的行的rowid,而不是明确提供rowid(s)。它可以减少我们的情况下的逻辑I/O数量(使用自动跟踪来验证)。

最后的查询可能是这样的(我没有验证,但只是想表达的想法):

delete from TABLE1 t1_1 
where C3_Date < :upper_date_bound 
    and C3_Date >= :lower_date_threshold 
    and (C1_Varchar2, C2_Varchar2, C3_Date) not in 
     (select C1_Varchar2, C2_Varchar2, max(C3_Date) 
      from table1 t1_2 
     where C3_Date < :upper_date_bound 
      and C3_Date >= :lower_date_bound 
     group by C1_Varchar2, C2_Varchar2) 

因为行被删除的数量少于一半表可以考虑使用另一个子查询而不是“NOT IN”的“IN”或“EXISTS”子句。例如,在列C3_Date创建本地索引,执行统计收集和在主查询

... 
exists (select null from table1 t1_2 
     where t1_2.C1_Varchar2 = t1_1.C1_Varchar2 
      and t1_2.C2_Varchar2 = t1_1.C2_Varchar2 
      and t1_2.C3_Date = t1_1.C3_Date 
      /* don't forget about partition selectivity hint */ 
      and t1_2.C3_Date < :upper_date_bound 
      and t1_2.C3_Date >= :lower_date_bound   
     group by t1_2.C1_Varchar2, t1_2.C2_Varchar2 
     having t1_1.C3_Date < max(t1_2.C3_Date)) 

尝试这部分 -

问候

+0

我看到查询耗尽临时表空间,但我从未见过其中一个生成ORA-4031。我仍然不相信这个问题甚至是关于性能。 –

-1

考虑插入在块(数据1天),为高效稳定的选择。

即使在TABLE1中只有12%的数据是巨大的(约2亿)!

当前状态的查询也会导致ORA-01555(读一致性)错误。它也将处理UNDO。

我还没有测试过这个示例代码,但它会给你一个想法,你怎么可以手动创建块(每天1)。

这个想法是在20个月之前得到最大和最小的C3_Date。从最短日期导航到最长日期。

尝试在'C3_Date'上添加一个合适的引导列以防止'INDEX SKIP SCAN'。这可能会有所帮助。祝你好运!

var i_days number ;        
SELECT (max(C3_Date) - min(C3_Date)) into :i_days from TABLE1 where C3_Date < ADD_MONTHS(SYSDATE, -20); 

-- CHANGE i_mig_start_date IN PROD, if required 
var i_mig_start_date varchar2(30); 
exec :i_mig_start_date := ADD_MONTHS(SYSDATE, -20)); 
--exec :i_mig_start_date := '03-OCT-2016 16:00:00'; 
declare 
    i number; 
    v_sql VARCHAR2(4000); 
    l_cmd_str VARCHAR2(4000); 
    l_from_datetime date; 
    l_to_datetime date; 
begin 
    -- Process chunk (1 day) 
    FOR i IN 1..:i_days LOOP 
     l_from_datetime := to_date(:i_mig_start_date,'DD-MON-YYYY HH24:MI:SS')-(i); 
     l_to_datetime := to_date(:i_mig_start_date,'DD-MON-YYYY HH24:MI:SS')-(i-1); 

     l_cmd_str := 'DELETE from /*+ PARALLEL(TABLE1 4)*/ TABLE1 
        where ROWID IN (SELECT rid from (SELECT ROWID rid, ROW_NUMBER() over (PARTITION BY C1_Varchar2,C2_Varchar2 ORDER BY C3_Date desc) as Rank 
               from TABLE1 where C3_Date > to_date(:l_from_datetime) and C3_Date <= to_date(:l_to_datetime)) 
            where Rank <> 1 
            )'; 
     DBMS_OUTPUT.PUT_LINE('Processing cycle i #'    || i || ' From: ' || l_from_datetime || ' To: ' || l_to_datetime) ; 
     DBMS_OUTPUT.PUT_LINE(l_cmd_str) ; 
     execute immediate l_cmd_str using l_from_datetime, l_to_datetime; 
     commit; 
    END LOOP;  
end; 
/
+0

我会很高兴我能得到建设性的反馈意见“为什么”的投票...谢谢! – pahariayogi