如何优化 Oracle 查询?

发布于 2024-12-26 06:03:32 字数 1968 浏览 2 评论 0原文

我收到一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

简美 2025-01-02 06:03:32

作为对查询的快速更新,您将需要将其重构为如下所示:

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_txtfirst_name_txtlast_name_txtbirth_dt。考虑在后三列上放置多列索引,在 soc_sec_num_txt 列上放置单列索引。

As a quick update to your query, you're going to want to refactor it to something like this:

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'
        )
    )

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 is null, and = check you do will return false for that row. In fact, any comparison with a null column, even null = null returns false. You can try this with select 1 where null = null and select 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 on evaluator_id? Does credit_request? What types are they? Typically, evaluator will have a one on the PK evaluator_id, and credit_request will have one for that column, as well. The same for requestor and the request_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 the soc_sec_num_txt column.

段念尘 2025-01-02 06:03:32

重构查询后出现索引,因此遵循 @eric 的帖子:

credit_request
您正在将其加入 request_id 上的 requestor,我希望它是唯一的。然后,在 where 子句中,您可以在 evaluator_id 上设置条件,并在查询中选择 client_app_idpersonal_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_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) ...等等这可能会提高速度,具体取决于列的选择性。这意味着,如果 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 on request_id, which I hope is unique. In your where clause you then have a condition on evaluator_id and select client_app_id and personal_flg in the query. So, you probably need a unique index, on credit_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 on evaluator_id, which is included in the first index.

requestor:
This is being joined onto on request_id and your where clause include soc_sec_num_text, lower(first_name_txt), lower(last_name_txt) and birth_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 selecting requestor_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 adding lower(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 than birth_dt you'd be better of putting this in front of birth_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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文