通过多个MAX子查询优化Oracle SQL Select

时间:2019-12-20 16:30:46

标签: sql oracle optimization

在Oracle 12c数据库中,我有以下查询:

SELECT pd.product_id,
       sd.column_1,
       sd.column_2,
(SELECT ROUND ( SUM (cost_1 + cost_2) / MAX (ref_coefficient), 4)
   FROM cost_reference
  WHERE product_name = sd.product_name)
     AS weighted_cost,
(SELECT cost_1 + cost_2
   FROM cost_reference
  WHERE product_name = sd.product_name 
    AND ref_coefficient = (SELECT MAX (ref_coefficient)
                              FROM cost_reference
                             WHERE product_name = sd.product_name ))
     AS normal_cost
   FROM sales_data sd, product_data pd
  WHERE sd.product_name = pd.product_name 
    AND sd.date = SYSDATE

cost_reference表用于根据当前时间加载的特定系数提取特定系数。 weighted_cost和normal_cost列是计算显示的子查询的。但是,多次调用MAX(ref_coefficient)确实会减慢此查询的速度。整个过程大约需要3秒钟,但是我们希望缩短该时间,因为这被称为不断刷新数据的地方,最终结果只有大约50行。 Cost_reference通常包含大约2500行。

适当的索引也在那里,因为不存在时大约花费了10秒钟。如果我取出这些计算出的列,则查询是即时的。我尝试加入cost_reference表或对其使用WITH语句,但我没有任何运气。有什么方法可以进一步优化此查询?

有趣的提示: 当我们将Dell Toad用于我们的SQL IDE时,我在其上运行了一个内置的优化工具,并注意到其中一个查询将时间缩短了一半。但是,在对其进行检查时,所做的一切只是将此注释放入normal_cost计算中。如果没有任何优化建议,有人知道为什么这样做有效吗?当我删除它时,它会恢复其较慢的行为。

SELECT /*+ FULL(COST_REFERENCE) */
      cost_1 + cost_2
 FROM cost_reference

2 个答案:

答案 0 :(得分:1)

使用正确的JOIN语法编写查询。我还建议限定 all 列名,尤其是在使用相关子查询时。

您可以重写第二个子查询,以便它使用聚合。而且由于sysdate具有时间成分,因此WHERE子句不太可能达到您的期望。

因此,我将查询重写为:

SELECT pd.product_id, sd.column_1, sd.column_2,
       (SELECT ROUND(SUM(cost_1 + cost_2) / MAX(ref_coefficient), 4)
        FROM cost_reference cr
        WHERE cr.product_name = sd.product_name
       ) AS weighted_cost,
       (SELECT MAX(cr.cost_1 + cr.cost_2) KEEP (DENSE_RANK FIRST ORDER BY ref_coefficient DESC)
        FROM cost_reference cr
        WHERE cr.product_name = sd.product_name 
       ) AS normal_cost
FROM sales_data sd JOIN
     product_data pd
     ON sd.product_name = pd.product_name 
WHERE sd.date = TRUNC(SYSDATE)

对于此查询,您需要以下索引:

  • sales_data(date, product_name)
  • product_data(product_name)
  • cost_reference(product_name, ref_coefficient, cost_1, cost_2)

答案 1 :(得分:1)

正如您所说的cost_reference有2.5k行,那么您可以尝试一下(

SELECT pd.product_id, sd.column_1, sd.column_2,
    weighted_cost,
    normal_cost
FROM sales_data sd JOIN
    product_data pd
    ON sd.product_name = pd.product_name 
    join (select product_name, ROUND(SUM(cost_1 + cost_2) / MAX(ref_coefficient), 4) weighted_cost,
    MAX(cr.cost_1 + cr.cost_2) KEEP (DENSE_RANK FIRST ORDER BY ref_coefficient DESC) normal_cost 
    from cost_reference cr
    group by product_name) cr on (cr.product_name=sd.product_name)
WHERE sd.date = TRUNC(SYSDATE)

这个想法是只包含cost_reference的一个集合,并且由于它很小,结果集也很小。注意:未使用这些表对SQL进行完整检查。