帮助进行SQL调优 - ORACLE

时间:2010-12-02 14:09:22

标签: sql oracle performance

我有一个查询从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';

有什么不对的,可以换成其他东西吗? 谢谢你的帮助

5 个答案:

答案 0 :(得分:2)

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

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

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 :(得分:1)

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

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

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

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

不能比那更具体:)

答案 2 :(得分:1)

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

答案 3 :(得分:1)

正如其他人所说,如果不看执行计划就很难说清楚。

但......我会关注的一些事情:

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

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

  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 值相同的条件,您甚至可以更好地重构这个以使用分析函数(SO questionOracle documentation)他们首先在查询中:

    b.amount

    显然,由于您在主查询和子查询中以不同方式加入TABLE3,因此您目前有不同的求和{{1}}标准,但我认为这更像是“不完整加入路径”的一个因素。通过有目的的设计(我猜测,因为我无法从代码本身告诉查询的意图)。

答案 4 :(得分:0)

当你将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”之后加入。