【问题标题】:How do I go about optimizing an Oracle query?如何优化 Oracle 查询?
【发布时间】:2012-01-11 19:02:55
【问题描述】:

给了我一个 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' 

【问题讨论】:

  • 我们可以引导您完成整个过程,但我们需要查看查询。每个查询都不同,但一般来说,您希望尽可能避免表/聚集索引扫描。阅读查询计划更像是一门艺术而不是科学:)
  • 我已经用查询更新了问题

标签: sql oracle oracle11g oracle-sqldeveloper


【解决方案1】:

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

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_idcredit_request 也会有一个用于该列。 requestorrequest_id 列也是如此。

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

【讨论】:

  • 你可以这样做 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')
  • @ShannonSeverance - 谢谢!自从我在 Oracle 工作以来已经有一段时间了。欣赏更正。我编辑了帖子。我今天学到了一些东西:)
  • 通常情况下,evaluator 将在 PK evaluator_id 上有一个聚集的,而 credit_request 将有一个非聚集的。 您是在谈论聚集索引吗?如果是这样,那就是 SQL Server 术语。 Oracle 聚簇表完全不同,并且不是默认值,并且根据我的经验并不典型。 (编辑以包括需要正确的否定。)
  • @ShannonSeverance - 再次感谢 - 自从我在 Oracle 工作以来已经有 2-3 年了。最近做了很多 SQL Server。
【解决方案2】:

在重构查询之后是索引,所以继续@eric 的帖子:

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,这意味着您已经从索引中选择了值,然后重新进入表格以获取更多信息。如果此信息已经在索引中,则不需要。

您正在将它加入到evaluator_id 上的evaluator,它包含在第一个索引中。

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_txt 中的值比 birth_dt 多得多,则最好将其放在索引中 birth_dt 的前面,这样如果查询不是唯一的,则需要扫描的查询更少索引。

您注意到我没有将所选列添加到此索引中,因为您已经必须进入表中,因此添加它不会获得任何收益。

evaluator:
这仅在evaluator_id 上加入,因此如果可能,您需要在此列上使用唯一的索引。

【讨论】:

    猜你喜欢
    • 2017-06-15
    • 2015-02-26
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多