如何优化 Oracle 查询?
我收到一个 SQL 查询,说我必须优化这个查询。
我遇到了解释计划
。因此,在 SQL Developer 中,我运行了解释计划,
它将查询划分为不同的部分并显示每个部分的成本。
我该如何优化查询?我要寻找什么?成本高的元素?
我对数据库有点陌生,所以如果您需要更多信息,请询问我,我会尽力获取。
我试图理解这个过程,而不是仅仅发布查询本身并获得答案。
有问题的查询:
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'
I was given a SQL query, saying that I have to optimize this query.
I came accross explain plan
. So, in SQL developer, I ran explain plan for ,
It divided the query into different parts and showed the cost for each of them.
How do I go about optimizing the query? What do I look for? Elements with high costs?
I am a bit new to DB, so if you need more information, please ask me, and I will try to get it.
I am trying to understand the process rather than just posting the query itself and getting the answer.
The query in question:
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))
On running explain plan, I am trying to upload the screenshot.
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'
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
作为对查询的快速更新,您将需要将其重构为如下所示:
首先,通过逗号连接会创建交叉连接,这是您希望避免的。幸运的是,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
在 PKevaluator_id
上会有一个,credit_request
也会在该列上有一个。requestor
和request_id
列也是如此。您可能需要考虑的其他索引是您用于过滤的所有字段。在本例中,为
soc_sec_num_txt
、first_name_txt
、last_name_txt
、birth_dt
。考虑在后三列上放置多列索引,在soc_sec_num_txt
列上放置单列索引。As a quick update to your query, you're going to want to refactor it to something like this:
Firstly, joining by commas creates a cross join, which you want to avoid. Luckily, Oracle's smart enough to do it as an inner join since you specified join conditions, but you want to be explicit so you don't accidentally miss something.
Secondly, your
is not null
checks are pointless--if a column isnull
, and=
check you do will return false for that row. In fact, any comparison with anull
column, evennull = null
returns false. You can try this withselect 1 where null = null
andselect 1 where null is null
. Only the second one returns.Thirdly, Oracle's smart enough to compare dates with the ISO format (at least the last time I used it, it was). You can just do
r.birth_dt = date '1960-01-02'
and avoid doing a string format on that column.That being said, your query isn't exactly poorly written in terms of egregious performance mistakes. What you want to look for are indices. Does
evaluator
have one onevaluator_id
? Doescredit_request
? What types are they? Typically,evaluator
will have a one on the PKevaluator_id
, andcredit_request
will have one for that column, as well. The same forrequestor
and therequest_id
columns.Other indices you may want to consider are all the fields you're using to filter. In this case,
soc_sec_num_txt
,first_name_txt
,last_name_txt
,birth_dt
. Consider putting a multi-column index on the latter three, and a single column index on thesoc_sec_num_txt
column.重构查询后出现索引,因此遵循 @eric 的帖子:
credit_request
:您正在将其加入
request_id
上的requestor
,我希望它是唯一的。然后,在 where 子句中,您可以在evaluator_id
上设置条件,并在查询中选择client_app_id
和personal_flg
。因此,您可能需要在credit_request
的(request_id, evaulator_id, client_app_id, individual_flg
) 上建立一个唯一索引。通过将您选择的列放入索引中,您可以避免 < 已在索引中,则无需。
code>by index rowid,这意味着您已从索引中选择了值,然后重新输入表以获取更多信息,如果此信息 将其加入到
evaluator_id
上的evaluator
上,该 ID 包含在第一个索引中requestor
:。
这是在
request_id
上加入的,您的 where 子句包括soc_sec_num_text
、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) ...等等
这可能会提高速度,具体取决于列的选择性。这意味着,如果first_name_txt
中的值远多于birth_dt
,那么最好将其放在birth_dt
的前面索引,这样如果它是非唯一索引,您的查询就需要扫描更少的内容。您注意到我没有将选定的列添加到该索引中,因为您已经必须进入表中,因此添加它不会获得任何好处。
评估者
:这仅在
evaluator_id
上加入,因此您需要在此列上有一个唯一的索引(如果可能)。After refactoring the query comes indexes, so following on from @eric's post:
credit_request
:You're joining this onto
requestor
onrequest_id
, which I hope is unique. In your where clause you then have a condition onevaluator_id
and selectclient_app_id
andpersonal_flg
in the query. So, you probably need a unique index, oncredit_request
of(request_id, evaulator_id, client_app_id, personal_flg
.By putting the columns you're selecting into the index you avoid the
by index rowid
, which means that you have selected your values from the index then re-entered the table to pick up more information. If this information is already in the index then there's no need.You're joining it onto
evaluator
onevaluator_id
, which is included in the first index.requestor
:This is being joined onto on
request_id
and your where clause includesoc_sec_num_text
,lower(first_name_txt)
,lower(last_name_txt)
andbirth_dt
. So, you need a unique if possible, index on(request_id, soc_sec_num_text)
because of the or this is further complicated because you should really have an index on as many of the conditions as possible. You're also selectingrequestor_type_iud
.In this case to avoid a functional index, with many columns, I'd index on
(request_id, soc_sec_num_text, birth_dt )
if you have the space, time and inclination then addinglower(first_name_txt)... etc
to this may improve the speed depending on how selective the column is. This means that if there are far more values in for instance,first_name_txt
thanbirth_dt
you'd be better of putting this in front ofbirth_dt
in the index so your query has less to scan if it's a non-unique index.You notice that I haven't added the selected column into this index as you're already going to have to go into the table so you gain nothing by adding it.
evaluator
:This is only being joined on
evaluator_id
so you need a unique, if possible, index on this column.