如何优化Oracle查询?

时间:2012-01-11 19:02:56

标签: sql oracle oracle11g oracle-sqldeveloper

我收到了一个SQL查询,说我必须优化这个查询。

我来到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' 

2 个答案:

答案 0 :(得分: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'并避免在该列上执行字符串格式。

话虽这么说,但是你的查询在严重的性能错误方面写得并不是很糟糕。你想要寻找的是指数。 evaluator上有一个evaluator_id吗?是credit_request吗?它们是什么类型的?通常情况下,evaluator会在PK evaluator_id上有一个,而credit_request也会有一个用于该列。 requestorrequest_id列的内容相同。

您可能要考虑的其他索引是您用来过滤的所有字段。在这种情况下,soc_sec_num_txtfirst_name_txtlast_name_txtbirth_dt。考虑在后三个上放置一个多列索引,在soc_sec_num_txt列上放一个列索引。

答案 1 :(得分:2)

重构后,查询会出现索引,所以继续@ eric的帖子:

<强> credit_request
您将requestor加入request_id,我希望这是唯一的。在您的where子句中,您在evaluator_id上有一个条件,并在查询中选择client_app_idpersonal_flg。因此,您可能需要credit_request (request_id, evaulator_id, client_app_id, personal_flg上的唯一索引。

通过将您选择的列放入索引中,可以避免使用by index rowid,这意味着您已从索引中选择了值,然后重新输入表以获取更多信息。如果此信息已经在索引中,那么就没有必要了。

您将其加入evaluator上的evaluator_id,该requestor包含在第一个索引中。

<强> request_id
这已加入soc_sec_num_text,您的where子句包括lower(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_txt可能会提高速度,具体取决于柱的选择性如何。这意味着如果有更多的值,例如birth_dt而不是birth_dt,那么您最好将其放在索引中的evaluator前面,这样您的查询就可以扫描更少了如果它是一个非唯一索引。

您注意到我没有将所选列添加到此索引中,因为您已经必须进入该表,因此您无需添加任何内容。

<强> evaluator_id
这仅在{{1}}上加入,因此您需要此列的唯一索引(如果可能)。