2012-01-11 121 views
3

我被給了一個SQL查詢,說我必須優化這個查詢。如何優化Oracle查詢?

我來了穿越explain plan。因此,在SQL開發人員中,我運行了解釋計劃:

它將查詢劃分爲不同的部分並顯示每個部分的成本。

如何優化查詢?我尋找什麼?成本高的元素?

我對DB有點新,所以如果你需要更多的信息,請問我,我會盡力得到它。

我想了解的過程,而不是隻發佈查詢本身,並得到答案。

有問題的查詢:

SELECT cr.client_app_id, 
    cr.personal_flg, 
    r.requestor_type_id 
FROM credit_request cr, 
    requestor r, 
    evaluator e 
WHERE cr.evaluator_id = 96 AND 
    cr.request_id = r.request_id AND 
    cr.evaluator_id = e.evaluator_id AND 
    cr.request_id != 143462 AND 
    ((r.soc_sec_num_txt = 'xxxxxxxxx' AND   
    r.soc_sec_num_txt IS NOT NULL) OR 
    (lower(r.first_name_txt) = 'test' AND 
    lower(r.last_name_txt) = 'newprogram' AND 
    to_char(r.birth_dt, 'MM/DD/YYYY') = '01/02/1960' AND 
    r.last_name_txt IS NOT NULL AND 
    r.first_name_txt IS NOT NULL AND 
    r.birth_dt IS NOT NULL)) 

在運行解釋計劃,我要上傳的截圖。

OPERATION OBJECT_NAME  OPTIONS  COST 
SELECT STATEMENT      15 
NESTED LOOPS    
NESTED LOOPS       15 
HASH JOIN        12 
Access Predicates 
CR.EVALUATOR_ID=E.EVALUATOR_ID 
INDEX EVALUATOR_PK  UNIQUE SCAN  0 
Access Predicates 
E.EVALUATOR_ID=96 
TABLE ACCESS CREDIT_REQUEST BY INDEX ROWID  11 
INDEX CRDRQ_DONE_EVAL_TASK_REQ_NDX  SKIP SCAN 10 
Access Predicates 
CR.EVALUATOR_ID=96 
Filter Predicates 
AND 
CR.EVALUATOR_ID=96 
CR.REQUEST_ID<>143462 
INDEX REQUESTOR_PK  RANGE SCAN  1 
Access Predicates 
CR.REQUEST_ID=R.REQUEST_ID 
Filter Predicates 
R.REQUEST_ID<>143462 
TABLE ACCESS REQUESTOR  BY INDEX ROWID  3 
Filter Predicates 
OR 
R.SOC_SEC_NUM_TXT='XXXXXXXX' 
AND 
R.BIRTH_DT IS NOT NULL 
R.LAST_NAME_TXT IS NOT NULL 
R.FIRST_NAME_TXT IS NOT NULL 
LOWER(R.FIRST_NAME_TXT)='test' 
LOWER(R.LAST_NAME_TXT)='newprogram' 
TO_CHAR(INTERNAL_FUNCTION(R.BIRTH_DT),'MM/DD/YYYY')='01/02/1960' 
+0

我們可以引導您完成整個流程,但我們需要查看查詢。每個查詢都不同,但通常情況下,您希望儘可能避免表/聚簇索引掃描。閱讀查詢計劃是比科學更藝術:) – Eric 2012-01-11 19:05:47

+0

我已更新問題與查詢 – roymustang86 2012-01-11 19:15:33

回答

2

重構查詢後自帶的索引,所以從@埃裏克的帖子下面就:

credit_request
你在request_id加入這個到requestor,我希望是獨一無二的。在您的where子句中,您在evaluator_id上有一個條件,並在查詢中選擇client_app_idpersonal_flg。所以,你可能需要一個唯一的索引,credit_request(request_id, evaulator_id, client_app_id, personal_flg

通過將您選擇的列放入索引中,您可以避開by index rowid,這意味着您已從索引中選擇了您的值,然後重新輸入表格以獲取更多信息。如果這些信息已經在索引中,那麼就沒有必要。

您正在將它加入evaluatorevaluator_id,它包含在第一個索引中。

requestor
這被加入到上request_id和你的where子句包括soc_sec_num_textlower(first_name_txt)lower(last_name_txt)birth_dt。所以,如果可能的話,你需要一個唯一的索引(request_id, soc_sec_num_text),因爲或者這更復雜,因爲你應該儘可能多的索引條件。您還選擇requestor_type_iud

在這種情況下,爲了避免函數索引,有很多列,如果您有空間,時間和傾向,那麼我會在(request_id, soc_sec_num_text, birth_dt)上索引,然後將lower(first_name_txt)... etc添加到這可能會提高速度,具體取決於列的選擇性。這意味着如果有更多的值,例如first_name_txtbirth_dt,那麼最好在birth_dt的前面放置索引,這樣如果查詢是非唯一索引,那麼查詢的掃描次數就會減少。

您注意到我沒有將選定的列添加到此索引中,因爲您已經必須進入表中,因此您無法通過添加它而獲得任何結果。

evaluator
這只是正在對evaluator_id加入,所以你需要在此列上的唯一,如果可能的話,指數。

3

作爲一個快速更新到您的查詢,你會想將它重構到這樣的事情:

SELECT 
    cr.client_app_id, 
    cr.personal_flg, 
    r.requestor_type_id 
FROM 
    credit_request cr 
    inner join requestor r on 
     cr.request_id = r.request_id 
    inner join evaluator e on 
     cr.evaluator_id = e.evaluator_id 
WHERE 
    cr.evaluator_id = 96 
    and cr.request_id != 143462 
    and (r.soc_sec_num_txt = 'xxxxxxxxx' 
     or (
      lower(r.first_name_txt) = 'test' 
      and lower(r.last_name_txt) = 'newprogram' 
      and r.birth_dt = date '1960-01-02' 
     ) 
    ) 

首先,用逗號連接創建了一個交叉連接,要避免。幸運的是,由於您指定了加入條件,因此Oracle非常聰明地將其作爲內部聯接來執行,但您希望保持清晰,以免意外錯過某些內容。

其次,您的is not null檢查是毫無意義的 - 如果列是null=檢查您是否會爲該行返回false。實際上,任何與null列的比較,即使null = null都會返回false。你可以試試select 1 where null = nullselect 1 where null is null。只有第二個返回。第三,Oracle的智能足以將日期與ISO格式進行比較(至少在我上次使用它的時候是這樣)。您只需執行r.birth_dt = date '1960-01-02'並避免在該列上執行字符串格式。

這就是說,您的查詢沒有完全寫得很差,表現出色的性能錯誤。你想要尋找的是指數。 evaluatorevaluator_id嗎? credit_request?他們是什麼類型的?通常,evaluator將在PK evaluator_id上有一個,而credit_request也會有一個用於該列。對於requestorrequest_id列也是如此。

其他可能需要考慮的指標是您用於過濾的所有字段。在這種情況下,soc_sec_num_txt,first_name_txt,last_name_txt,birth_dt。請考慮在後三個列中添加多列索引,並在soc_sec_num_txt列中添加單列索引。

+3

_您可以只''r.birth_dt ='1960-01-02''_這將默認將字符串轉換爲基於日期在NLS_DATA_FORMAT上。因此,無論是否有效,都取決於不同的會話設置。可以使用'date'關鍵字來使用日期常量,比如'r.birth_dt = date'1960-01-02''或'to_date',其格式如'r.birth_dt = to_date('1960-01- 02','YYYY-MM-DD')' – 2012-01-11 19:35:31

+0

@ShannonSeverance - 謝謝!自從我在甲骨文工作了一段時間。欣賞更正。我編輯了這篇文章。並且我今天學到了一些東西:) – Eric 2012-01-11 19:38:27

+0

_通常,評估者將在PK evaluateator_id上具有一個聚類,而credit_request將具有非聚類._您是在談論聚簇索引嗎?如果是這樣,那是一個SQL Server術語。 Oracle聚簇表是完全不同的,並且**不是默認的,在我的經驗中並不典型。 (編輯包括負面需要是正確的。) – 2012-01-11 19:38:30