【问题标题】:Help for SQL tuning - ORACLESQL 调优帮助 - ORACLE
【发布时间】:2011-05-19 03:54:13
【问题描述】:

我有一个从 5 个大表中获取数据的查询,请您帮我调整一下这个查询的性能:

SELECT DECODE(SIGN((t1.amount - NVL(t2.amount, 0)) - 4.999), 1, NVL(t2.amount, 0), t1.amount) AS amount_1,
       t1.element_id,
       t1.start_date ,
       t1.amount,
       NVL(t5.abrev, NULL) AS criteria,
       t1.case_id ,
       NVL(t5.value, NULL) segment,
       add_months(t1.start_date, -1) invoice_date,
       NVL((SELECT SUM(b.amount)
             FROM TABLE1 a, TABLE3 b
            WHERE a.element_id = b.element_id
              AND b.date_invoicing < a.start_date
              AND t1.element_id = a.element_id),
           0) amount_2
  FROM TABLE1 t1, TABLE2 t2, TABLE3 t3, TABLE4 t4, TABLE5 t5
 WHERE t1.TYPE = 'INVOICE'
   AND t2.case_id = t3.case_id
   AND t2.invoicing_id = t3.invoicing_id
   AND t2.date_unpaid IS NULL
   AND t1.element_id = t3.element_id(+)
   AND add_months(t1.start_date, -1) <
       NVL(t4.DT_FIN_DT(+), SYSDATE)
   AND add_months(t1.start_date, -1) >= t4.date_creation(+)
   AND t1.case_id = t4.case_id(+)
   AND t4.segment = t5.abrev(+)
   AND t5.Type(+) = 'CRITERIA_TYPE';

有什么问题可以换成别的吗?
感谢您的帮助

【问题讨论】:

  • 就目前而言,您尝试使用 (+) 到 t3 的外部连接,但被其他不使用 (+) 的谓词强制返回到内部连接,例如“t2.case_id = t3 。案例ID”。如果您可以使用 ANSI 连接来编写它,那么它会更不容易出错并且更易于阅读。不过,这与性能无关。

标签: sql oracle performance


【解决方案1】:

您必须做的第一件事是使用显式连接。这会将您的连接与过滤器分开,并帮助您更好地调整它。

请检查这些连接是否正确。

SELECT 
    DECODE(SIGN((t1.amount - NVL(t2.amount, 0)) - 4.999), 1, NVL(t2.amount, 0), t1.amount) AS amount_1,
    t1.element_id,
    t1.start_date ,
    t1.amount,
    NVL(t5.abrev, NULL) AS criteria,
    t1.case_id ,
    NVL(t5.value, NULL) segment,
    add_months(t1.start_date, -1) invoice_date,
    NVL
    (
        (SELECT SUM(b.amount)
        FROM TABLE1 a, TABLE3 b
        WHERE a.element_id = b.element_id
        AND b.date_invoicing < a.start_date
        AND t1.element_id = a.element_id),
    0) amount_2
FROM 
    TABLE1 t1

    LEFT OUTER JOIN TABLE3 t3
        on t1.element_id = t3.element_id

    INNER JOIN TABLE2 t2, 
        on t2.invoicing_id = t3.invoicing_id
        and t2.case_id = t3.case_id

    LEFT OUTER JOIN TABLE4 t4 
        on t1.case_id = t4.case_id

    LEFT OUTER JOIN TABLE5 t5
        on t4.segment = t5.abrev

WHERE t1.TYPE = 'INVOICE'
    AND t2.date_unpaid IS NULL
    AND add_months(t1.start_date, -1) < NVL(t4.DT_FIN_DT(+), SYSDATE)
    AND add_months(t1.start_date, -1) >= t4.date_creation(+)
    AND t5.Type(+) = 'CRITERIA_TYPE';

如果是,那么您可以做几件事,但最好的办法是查看执行计划。

【讨论】:

  • 在性能方面应该是完全一样的。分离可帮助您更好地确定要调整的内容。
  • +1 用于建议 ANSI 样式的连接,但实际上应该是 LEFT OUTER JOIN 而不仅仅是 OUTER JOIN
  • @BQ 我更正了。它曾经是我在面试中最喜欢问的问题之一——LEFT JOIN 和 LEFT OUTER JOIN 之间的区别。它肯定让很多人失望,在某些情况下,我们得到了一些非常有创意的答案。 :)
【解决方案2】:

正如其他人所指出的,不看执行计划就很难判断。

但是……有些事情我会关心:

  1. 主查询中 TABLE3 的外部连接并不完整,正如@TonyAndrews 在上面的评论中提到的那样。请参阅Common errors seen when using OUTER-JOIN 上的“不完整的加入跟踪”示例。 这意味着您的查询可能会产生错误的结果,但在不了解查询的全部意图和架构的情况下,除了您之外没有人可以肯定地知道这一点。

    更新您的查询以使用 Oracle 样式 TableName.ColumnName(+) 中的 ANSI 样式 INNER/[LEFT|RIGHT] OUTER 语法将有助于使这一点更加明显。

  2. 标量子查询将为每一行运行并且可能很慢(假设 TABLE3 很大)。如果TABLE3.element_idTABLE3.date_invoicing 上没有有用的索引会非常慢:

    NVL((SELECT SUM(b.amount)
         FROM TABLE1 a, TABLE3 b
         WHERE a.element_id = b.element_id
           AND b.date_invoicing < a.start_date
           AND t1.element_id = a.element_id),
        0) amount_2
    

    因此,我认为不需要在此子查询中再次包含 TABLE1。最好将其重构为:

    NVL((SELECT SUM(b.amount)
         FROM TABLE3 b
         WHERE t1.element_id = b.element_id
           AND b.date_invoicing < t1.start_date,
        0) amount_2
    

    或者,如果对 b.amount 值求和的条件与将它们包含在第一名:

    SUM(b.amount) OVER (PARTITION BY b.element_id) amount_2
    

    显然,您目前对 b.amount 求和的标准不同,因为您在主查询和子查询中加入 TABLE3 的方式不同,但我想这更多是“不完整的加入跟踪”的一个因素,而不是有目的的设计(我的猜测,因为我无法从代码本身判断查询的意图)。

【讨论】:

    【解决方案3】:

    优化器可能产生了一个次优的执行计划。或者考虑到数据库实际需要做的工作量,它可能会尽可能快地运行。 如果没有解释计划,知道键、关系和索引就很难知道发生了什么。

    选择列表中的标量子查询通常在外部查询返回大量行时不是一个好主意。

    以下表达式可能会因为函数调用而阻止优化器使用统计信息。出于同样的原因,可能也不会使用索引。

    AND add_months(t1.start_date, -1) < NVL(t4.DT_FIN_DT(+), SYSDATE)
    AND add_months(t1.start_date, -1) >= t4.date_creation(+)
    

    真的不能比这更具体:)

    【讨论】:

      【解决方案4】:

      您需要了解如何查看和理解执行计划。 This previous question 是一个很好的起点。

      【讨论】:

      • 真正有用的评论。
      【解决方案5】:

      当您将 Select 语句嵌套在另一个语句中时,这有点奇怪

      NVL((SELECT SUM(b.amount)
                   FROM TABLE1 a, TABLE3 b
                  WHERE a.element_id = b.element_id
                    AND b.date_invoicing < a.start_date
                    AND t1.element_id = a.element_id),
                 0) amount_2
      

      你需要重新写成表格并在“From”之后加入。

      【讨论】:

        猜你喜欢
        • 2011-05-12
        • 1970-01-01
        • 2013-09-16
        • 2010-11-16
        • 1970-01-01
        • 2012-01-06
        • 2014-02-06
        • 2011-03-01
        • 1970-01-01
        相关资源
        最近更新 更多