Oracle SQL - 任何人都可以提高效率吗?

Posted

技术标签:

【中文标题】Oracle SQL - 任何人都可以提高效率吗?【英文标题】:Oracle SQL - Can anyone make this more effiecient? 【发布时间】:2014-04-23 08:15:13 【问题描述】:

有点味道 - 这是从水晶报告中调用的查询,该报告使用被分成两个纸箱的纸箱编号进行搜索,并返回两个纸箱编号、它们的数量、从原始报告中删除的数量,用户和拖车号(如果它已经加载到一个上)。

查询的第一部分效果很好,不幸的是所有数据都在 30 天后存档,而且存档量很大!添加存档部分后,此查询可能需要 30 到 45 分钟才能运行,这太长了。谁能帮我优化这个查询,让它运行得更快?

非常感谢,7's。

SELECT *
FROM
  (SELECT menu_optn_name,
          tran_nbr,
          seq_nbr,
          cntr_nbr,
          ch.total_qty ,
          ptt.create_date_time,
          um.user_name,
          ch.trlr_nbr,

     (SELECT sum(ref_field_2)
      FROM prod_trkg_tran
      WHERE menu_optn_name = 'RF Split/Comb Carton'
        AND cntr_nbr = '0030651942') AS Total
   FROM prod_trkg_tran ptt
   INNER JOIN user_master um ON um.emplye_id = ptt.user_id
   LEFT OUTER JOIN carton_hdr ch ON ch.carton_nbr = ptt.cntr_nbr
   WHERE menu_optn_name = 'RF Split/Comb Carton'
     AND ptt.cntr_nbr = '0030651942'
   GROUP BY menu_optn_name,
            tran_nbr,
            seq_nbr,
            cntr_nbr,
            ch.total_qty,
            ptt.create_date_time,
            um.user_name,
            ch.trlr_nbr
   UNION SELECT menu_optn_name,
                tran_nbr,
                seq_nbr,
                cntr_nbr,
                ch.total_qty,
                ptt.create_date_time,
                um.user_name,
                ch.trlr_nbr,

     (SELECT sum(ref_field_2)
      FROM prod_trkg_tran
      WHERE menu_optn_name = 'RF Split/Comb Carton'
        AND cntr_nbr NOT IN
          (SELECT cntr_nbr
           FROM prod_trkg_tran ptt
           LEFT OUTER JOIN carton_hdr ch ON ch.carton_nbr = ptt.cntr_nbr
           WHERE menu_optn_name = 'RF Split/Comb Carton'
             AND ptt.cntr_nbr = '0030651942')
        AND tran_nbr IN
          (SELECT tran_nbr
           FROM prod_trkg_tran ptt
           LEFT OUTER JOIN carton_hdr ch ON ch.carton_nbr = ptt.cntr_nbr
           WHERE menu_optn_name = 'RF Split/Comb Carton'
             AND ptt.cntr_nbr = '0030651942')) AS Total
   FROM prod_trkg_tran ptt
   INNER JOIN user_master um ON um.emplye_id = ptt.user_id
   LEFT OUTER JOIN carton_hdr ch ON ch.carton_nbr = ptt.cntr_nbr
   WHERE cntr_nbr NOT IN
       (SELECT cntr_nbr
        FROM prod_trkg_tran ptt
        LEFT OUTER JOIN carton_hdr ch ON ch.carton_nbr = ptt.cntr_nbr
        WHERE menu_optn_name = 'RF Split/Comb Carton'
          AND ptt.cntr_nbr = '0030651942')
     AND tran_nbr IN
       (SELECT tran_nbr
        FROM prod_trkg_tran ptt
        LEFT OUTER JOIN carton_hdr ch ON ch.carton_nbr = ptt.cntr_nbr
        WHERE menu_optn_name = 'RF Split/Comb Carton'
          AND ptt.cntr_nbr = '0030651942')
   GROUP BY menu_optn_name,
            tran_nbr,
            seq_nbr,
            cntr_nbr,
            ch.total_qty,
            ptt.create_date_time,
            um.user_name,
            ch.trlr_nbr)
UNION
SELECT *
FROM
  (SELECT menu_optn_name,
          tran_nbr,
          seq_nbr,
          cntr_nbr,
          ch.total_qty,
          ptt.create_date_time,
          um.user_name,
          ch.trlr_nbr,

     (SELECT sum(ref_field_2)
      FROM wm_archive.prod_trkg_tran@awm.corp.*******.com
      WHERE menu_optn_name = 'RF Split/Comb Carton'
        AND cntr_nbr = '0030651942') AS Total
   FROM wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
   INNER JOIN user_master um ON um.emplye_id = ptt.user_id
   LEFT OUTER JOIN carton_hdr ch ON ch.carton_nbr = ptt.cntr_nbr
   WHERE menu_optn_name = 'RF Split/Comb Carton'
     AND ptt.cntr_nbr = '0030651942'
   GROUP BY menu_optn_name,
            tran_nbr,
            seq_nbr,
            cntr_nbr,
            ch.total_qty,
            ptt.create_date_time,
            um.user_name,
            ch.trlr_nbr
   UNION SELECT menu_optn_name,
                tran_nbr,
                seq_nbr,
                cntr_nbr,
                ch.total_qty,
                ptt.create_date_time,
                um.user_name,
                ch.trlr_nbr,

     (SELECT sum(ref_field_2)
      FROM wm_archive.prod_trkg_tran@awm.corp.*******.com
      WHERE menu_optn_name = 'RF Split/Comb Carton'
        AND cntr_nbr NOT IN
          (SELECT cntr_nbr
           FROM wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
           LEFT OUTER JOIN wm_archive.carton_hdr@awm.corp.*******.com ch ON ch.carton_nbr = ptt.cntr_nbr
           WHERE menu_optn_name = 'RF Split/Comb Carton'
             AND ptt.cntr_nbr = '0030651942')
        AND tran_nbr IN
          (SELECT tran_nbr
           FROM wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
           LEFT OUTER JOIN wm_archive.carton_hdr@awm.corp.*******.com ch ON ch.carton_nbr = ptt.cntr_nbr
           WHERE menu_optn_name = 'RF Split/Comb Carton'
             AND ptt.cntr_nbr = '0030651942')) AS Total
   FROM wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
   INNER JOIN user_master um ON um.emplye_id = ptt.user_id
   LEFT OUTER JOIN wm_archive.carton_hdr@awm.corp.*******.com ch ON ch.carton_nbr = ptt.cntr_nbr
   WHERE cntr_nbr NOT IN
       (SELECT cntr_nbr
        FROM wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
        LEFT OUTER JOIN wm_archive.carton_hdr@awm.corp.*******.com ch ON ch.carton_nbr = ptt.cntr_nbr
        WHERE menu_optn_name = 'RF Split/Comb Carton'
          AND ptt.cntr_nbr = '0030651942')
     AND tran_nbr IN
       (SELECT tran_nbr
        FROM wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
        LEFT OUTER JOIN wm_archive.carton_hdr@awm.corp.*******.com ch ON ch.carton_nbr = ptt.cntr_nbr
        WHERE menu_optn_name = 'RF Split/Comb Carton'
          AND ptt.cntr_nbr = '0030651942')
   GROUP BY menu_optn_name,
            tran_nbr,
            seq_nbr,
            cntr_nbr,
            ch.total_qty,
            ptt.create_date_time,
            um.user_name,
            ch.trlr_nbr)
WHERE rownum <=2;

【问题讨论】:

你为什么到处使用 SELECT SUM(ref_field_2) FROM...?看起来您正在执行子选择来汇总父选择中聚合的相同行。 第一次使用是在第一次选择纸箱号时使用,第二次使用是查找不是第一次号的纸箱号。它在每个查询中恰好使用两次。 这不是我要说的...每个单独的 subSELECT SUM FROM... 使用的是在相应的父选择中聚合的相同行...为什么? 我不确定我明白你的意思。 SELECT SUM(ref_field_2) 对查询结果中的所有行求和,因为将返回超过 2 行,我只对前 2 行感兴趣,除了 ref_field_2 之外的所有数据都必须合并。 您能告诉我哪些列位于哪个表上吗?我对 menu_opt_name、cntr_nbr 和 tran_nbr 感兴趣。另一件事...条件 menu_optn_name = 'RF Split/Comb Carton' 和 ptt.cntr_nbr = '0030651942' 隔离了多少行? 【参考方案1】:

我看到此查询有两种可能的优化。

第一个是:

SELECT key, (SELECT SUM(value) FROM table WHERE condition) AS Total
FROM table WHERE condition
GROUP BY key

在功能上等同于:

SELECT key, SUM(value)
FROM table WHERE condition
GROUP BY key

...但效率可能低得多。

第二个是:

SELECT x,y,z FROM table WHERE condition_1 GROUP BY x,y
UNION
SELECT x,y,z FROM table WHERE condition_2 GROUP BY x,y

在功能上等同于:

SELECT x,y,z FROM table WHERE condition_1 OR condition_2 GROUP BY x,y

也就是说,如果 condition_1 和 condition_2 只引用出现在 GROUP BY 子句中的列,就像你的情况一样。顺序上可能存在一些差异,但如有必要,可以使用显式 ORDER BY 子句解决。

也就是说,消除 4 个子选择和 2 个并集似乎是可能的。考虑到 WHERE 过滤器的所有列都位于 prod_trkg_tran 表中,您还可以删除一些 LEFT JOIN。所以它看起来像这样:

   select * from (
         select menu_optn_name, tran_nbr, seq_nbr, cntr_nbr, ch.total_qty, ptt.create_date_time, um.user_name, ch.trlr_nbr, sum(ref_field_2) as Total
         from            prod_trkg_tran ptt
         inner join      user_master    um  on um.emplye_id = ptt.user_id
         left outer join carton_hdr     ch  on ch.carton_nbr = ptt.cntr_nbr
         where (menu_optn_name = 'RF Split/Comb Carton' and ptt.cntr_nbr = '0030651942') or (
            cntr_nbr not in (
               select cntr_nbr
               from prod_trkg_tran
               where menu_optn_name = 'RF Split/Comb Carton' and cntr_nbr = '0030651942'
            ) and
            tran_nbr in (
               select tran_nbr
               from prod_trkg_tran 
               where menu_optn_name = 'RF Split/Comb Carton' and cntr_nbr = '0030651942'
            )
         )
         group by menu_optn_name, tran_nbr, seq_nbr, cntr_nbr, ch.total_qty, ptt.create_date_time, um.user_name, ch.trlr_nbr
   )
union
   select * from (
         select menu_optn_name, tran_nbr, seq_nbr, cntr_nbr, ch.total_qty, ptt.create_date_time, um.user_name, ch.trlr_nbr, sum(ref_field_2) as Total
         from            wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
         inner join      user_master                                    um  on um.emplye_id = ptt.user_id
         left outer join carton_hdr                                     ch  on ch.carton_nbr = ptt.cntr_nbr
         where (menu_optn_name = 'RF Split/Comb Carton' and ptt.cntr_nbr = '0030651942') or (
            cntr_nbr not in (
               select cntr_nbr
               from wm_archive.prod_trkg_tran@awm.corp.*******.com
               where menu_optn_name = 'RF Split/Comb Carton' and cntr_nbr = '0030651942'
            ) and
            tran_nbr in (
               select tran_nbr
               from wm_archive.prod_trkg_tran@awm.corp.*******.com ptt
               where menu_optn_name = 'RF Split/Comb Carton' and cntr_nbr = '0030651942'
            )
         )
         group by menu_optn_name, tran_nbr, seq_nbr, cntr_nbr, ch.total_qty, ptt.create_date_time, um.user_name, ch.trlr_nbr
   )
where rownum <=2;

还有第三个优化。 user_master 上的 INNER JOIN 可以从内部选择移出到外部联合。但我怀疑这次加入的成本很高。

【讨论】:

以上是关于Oracle SQL - 任何人都可以提高效率吗?的主要内容,如果未能解决你的问题,请参考以下文章

oracle如何定位效率较低的sql

如何提高sql2000全文检索搜索效率

oracle中sql问的大写与小写会影响sql语句的效率吗?

将 SQL 查询转换为 PL/SQL 可以提高 Oracle 12c 中的性能吗? [关闭]

oracle中的存储过程有啥作用,该怎么理解?(数据更新的话用update语句不就完了吗)

ORCLE-Select 语句执行顺序以及如何提高Oracle 基本查询效率