业务反应慢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还可以进一步优化,需要业务配合。
利用分区表的特性,加上必要的分区条件,这样只需扫描必要的分区索引就行。