我收到了一个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'
答案 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 = null
和select 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
也会有一个用于该列。 requestor
和request_id
列的内容相同。
您可能要考虑的其他索引是您用来过滤的所有字段。在这种情况下,soc_sec_num_txt
,first_name_txt
,last_name_txt
,birth_dt
。考虑在后三个上放置一个多列索引,在soc_sec_num_txt
列上放一个列索引。
答案 1 :(得分:2)
重构后,查询会出现索引,所以继续@ eric的帖子:
<强> credit_request
强>
您将requestor
加入request_id
,我希望这是唯一的。在您的where子句中,您在evaluator_id
上有一个条件,并在查询中选择client_app_id
和personal_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}}上加入,因此您需要此列的唯一索引(如果可能)。