2013-04-03 63 views
4

我在程序中遇到了一些麻煩,當運行「大」集(800+父母,1300+孩子),它是非常慢(30 - 60秒)如何優化流水線,弱類型遊標的使用

基本想法是獲取適合特定搜索條件的所有父記錄(及其各自的子女)以及需要計算的3條附加信息。

我對這個問題的方法是

  1. 創建與計算值附加字段自定義記錄型。
  2. 然後可以將對該記錄類型的引用傳遞給每個由主處理函數控制的函數。
  3. 由於計算每個父記錄的值,所以要將其記錄到記錄中。

每個過程GET_PARENT_RECORDSGET_CHILD_RECORDS被稱爲每次搜索一次,並且每個計算功能運行N次(其中N是親本和/或子記錄的數量)。


問題1:這是正確的做法? (弱類型遊標,流水線函數)如果不是,那麼我應該如何處理這個問題,假設我可以重新做呢?

問題2:禁止完全重寫,是否有任何明顯的可以改進的代碼提供?

問題3:或者可能是別的什麼錯誤,因爲我注意到,當我運行幾次程序時,在20秒內返回相同的慢查詢?


包定義

create or replace 
PACKAGE THIS_PKG AS 

    Type parentCursor IS REF CURSOR; 
    Type childCursor IS REF CURSOR; 

    Type ParentRecordType IS RECORD (
    other_columns, 
    Extra_column_A, 
    Extra_column_B, 
    Extra_column_C, 
    Row_num);  

    --associative array 
    TYPE ParentArray IS TABLE OF ParentRecordType; 

    FUNCTION processParents(
     p IN THIS_PKG. parentCursor 
) RETURN ParentArray 
    PIPELINED 
    ; 

    FUNCTION countSomething(some params…) 
     RETURN INT; 

    FUNCTION checkCondX (SomeParent IN ParentRecordType) 
     RETURN VARCHAR2; 

    FUNCTION checkCondY (SomeParent IN ParentRecordType) 
     RETURN VARCHAR2; 

    PROCEDURE GET_PARENT_RECORDS(other_parameters, Parents OUT THIS_PKG.parentCursor); 

    PROCEDURE GET_CHILD_RECORDS(other_parameters, Children OUT THIS_PKG.childCursor); 

END THIS_PKG; 

包體

-- omitted 

FUNCTION processParents(
     p IN THIS_PKG.parentCursor 
) RETURN ParentArray 
    PIPELINED 
    IS 
     out_rec ParentArray; 
     someParent ParentRecordType; 
    BEGIN 
    LOOP 
     FETCH p BULK COLLECT INTO out_rec LIMIT 100; 

     FOR i IN 1 .. out_rec.COUNT 
     LOOP 
     out_rec(i).extra_column_A := countSomething (out_rec(i).field1, out_rec(i).field2); 
     out_rec(i).extra_column_B := checkCondX(out_rec(i)); 
     out_rec(i).extra_column_C := checkCondY(out_rec(i)); 
     pipe row(out_rec(i)); 
     END LOOP; 

     EXIT WHEN p%NOTFOUND; 
    END LOOP; 
    RETURN; 
    END processParents; 

PROCEDURE GET_PARENT_RECORDS(
     some_columns, 
     Parents OUT THIS_PKG. parentCursor) IS 
    BEGIN 
     OPEN Parents FOR 
     SELECT * 
     FROM TABLE(processParents (CURSOR(
     SELECT * 
     FROM (
       --some select statement with quite a few where clause 
      --to simulate dynamic search (from pre-canned search options) 
     ) 
    ))) abc 
     WHERE abc.extra_column_C like '%xyz%' --(xyz is a user given value) 
     ; 
END GET_PARENT_RECORDS; 

更新 一些探索昨天DID和整個任務批處理SQL優化器(蟾蜍)來了。我插入包裝,這是我得到的。

批優化結果 Batch Optimizer results

複雜的查詢 Complex query

有問題的查詢 Problematic query

+1

你有沒有做過任何分析以指出問題出在哪裏? – 2013-04-03 23:29:18

+0

@BobJarvis我懷疑我可以做一個全面的分析上,因爲我不是DBA和可能難以挖掘他的時間。但是我對查詢進行了一些分析並附上了兩個解釋計劃。這是你在說什麼嗎? – user1766760 2013-04-04 18:02:36

+1

從不「禁止一個完全重寫的」,當涉及到SQL(或PL/SQL)。流水線是一個非常棒的功能,但通常可以通過改進的聲明式方法來消除。 – 2013-11-08 21:24:58

回答

0

任務批處理SQL優化器(蟾蜍)或任何其他工具將無法幫你考慮到他們不明白你在功能裏面做了什麼。問題出現在「FETCH p BULK COLLECT INTO out_rec LIMIT 100;」中。傳遞給p的查詢的質量實際上定義了最終的執行計劃和運行時間。流水線不是緩慢的原因。當您多次運行您的過程時,Oracle使用高速緩存的數據。我最好的建議是:使用Java而不是PL/SQL來實現這個特定目的,理解起來會更簡單。

1

什麼是在該行的處理部分發生了什麼?這些countSomething,checkCondX/Y函數可能會花費很多時間。他們是否也在進行SQL調用?我會首先檢查表函數的性能,而不用額外的謂詞。這可能是更好的只是創建一個查詢該做這一切,SQL,而不是功能 - 如果你能做到這一點,將是非常快很多比呼喚每一行的功能。

out_rec(i).extra_column_A := countSomething (out_rec(i).field1, out_rec(i).field2); 
    out_rec(i).extra_column_B := checkCondX(out_rec(i)); 
    out_rec(i).extra_column_C := checkCondY(out_rec(i)); 

您提供的解釋計劃也很有趣,因爲優化器認爲從所有表(基數1)中只返回1行。如果情況並非如此,那麼查詢計劃將不是最佳的。可能需要收集統計數據,在表格​​功能上使用動態採樣或cardinality hints

最後,看看DBMS_SQLTUNE.REPORT_SQL_MONITOR提供有關您的SQL的詳細報告。除非查詢被動態識別爲需要監視,否則需要添加/ * + MONITOR * /提示。這提供了更多的細節,例如返回的行數,執行計數以及解釋計劃中不可用的其他有趣花絮。

SELECT /*+ MONITOR */ 
FROM slow_query; 

-- then run sqltune to get a report 
SELECT * 
FROM TABLE(DBMS_SQLTUNE.REPORT_SQL_MONITOR());