2015-10-20 80 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