2012-01-11 77 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