SQL优化案例(union问题)

业务反应慢sql,sql_id为6dvj1fy4n5f6a。
通过v$sql_monitor视图查看慢sql的详细信息。
信息如下:
SQL Monitoring Report

SQL Text
------------------------------
SELECT MAX(rebuild_id) rebuild_id
FROM (SELECT rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE (a.asset_no = :1 OR a.resource_no = :2 OR a.bar_code = :3)
UNION
SELECT a.rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.box_id IN
(SELECT a.container_id
FROM c_container_dev a
WHERE a.equip_id IN
(SELECT meter_id
FROM d_meter
WHERE bar_code = :4
UNION ALL
SELECT meter_id FROM d_meter WHERE asset_no = :5)))

 

 

 

 

 

 

从sql逻辑上我们可以把上面的sql按照union分为两部分。
SELECT rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE (a.asset_no = '33xxx3' OR
a.resource_no = '33xxx3' OR
a.bar_code = '33XXX3')

SELECT a.rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.box_id IN
(SELECT a.container_id
FROM c_container_dev a
WHERE a.equip_id IN
(SELECT meter_id
FROM d_meter
WHERE bar_code = :4
UNION ALL
SELECT meter_id FROM d_meter WHERE asset_no = :5)

上部分sql过滤条件是三个or条件组合一起。
通过比较执行计划中的e-row和a-rows我们发现大致一样,所以统计信息基本准确。
通过表的统计信息发现sgpm_out.d_defect_rebuild表为60多万的数据量。
通过列统计信息发现asset_no,resource_no,bar_code三列的选择性都很高,resource_no,bar_code列存在索引。
因此该sql我们可以直接在asset_no列创建索引。
sql换成下面的写法:
SELECT rebuild_id
FROM sgpm_out.d_defect_rebuild a WHERE a.asset_no = '33XXX3'
union
select rebuild_id
FROM sgpm_out.d_defect_rebuild a WHERE a.resource_no = '33XXX3'
union
select rebuild_id
FROM sgpm_out.d_defect_rebuild a WHERE a.bar_code = '33XXX3'

下部分sql分析:
执行计划中

 

 

这个步骤耗费很多资源和时间,且返回行数较多。
SELECT a.rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.box_id IN
(SELECT a.container_id
FROM c_container_dev a
WHERE a.equip_id IN
(SELECT meter_id
FROM d_meter
WHERE bar_code = :4
UNION ALL
SELECT meter_id FROM d_meter WHERE asset_no = :5)

查看分区表C_CONTAINER_DEV表的统计信息
TABLE_NAME NUM_ROWS BLOCKS LAST_ANALYZED
C_CONTAINER_DEV 28015670 376461 2019/8/28 2:37:33

表分区列为:ORG_NO

C_CONTAINER_DEV表过滤条件为equip_id,equip_id列存在分区索引且选择性很好。

(SELECT meter_id
FROM d_meter
WHERE bar_code = :4
UNION ALL
SELECT meter_id FROM d_meter WHERE asset_no = :5)

该sql结果集较少。

 

很明显执行计划错误,驱动表有问题

应该拿

(SELECT meter_id
FROM d_meter
WHERE bar_code = :4
UNION ALL
SELECT meter_id FROM d_meter WHERE asset_no = :5)

结果集去驱动C_CONTAINER_DEV,

上述sql未能使用子查询非嵌套的查询改写。

sql改写如下:
SELECT a.rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.box_id IN (SELECT a.container_id
FROM c_container_dev a,
(SELECT meter_id
FROM d_meter
WHERE bar_code = '33XXX3'
UNION ALL
SELECT meter_id FROM d_meter WHERE asset_no = '33XXX3') b
WHERE a.equip_id = b.meter_id)

将sql整合一起:
select MAX(rebuild_id)
from (SELECT rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.asset_no = '33XXX3'
union
select rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.resource_no = '33XXX3'
union
select rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.bar_code = '33XXX3'
union
SELECT a.rebuild_id
FROM sgpm_out.d_defect_rebuild a
WHERE a.box_id IN (SELECT a.container_id
FROM c_container_dev a,
(SELECT meter_id
FROM d_meter
WHERE bar_code = '33XXX3'
UNION ALL
SELECT meter_id
FROM d_meter
WHERE asset_no = '33XXX3') b
WHERE a.equip_id = b.meter_id));

 

 

查询速度为毫秒级别。
上述sql还可以进一步优化,需要业务配合。
利用分区表的特性,加上必要的分区条件,这样只需扫描必要的分区索引就行。

posted @ 2020-06-04 09:10  阿西吧li  阅读(1092)  评论(0编辑  收藏  举报