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问的大写与小写会影响sql语句的效率吗?
将 SQL 查询转换为 PL/SQL 可以提高 Oracle 12c 中的性能吗? [关闭]