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

  • 相关阅读:
    自动化运维-ansible
    Linux中安装wget命令
    Celery
    SQLAlchemy
    非关系型数据库--MongoDB
    Flask框架
    Redis
    公钥与私钥
    win10 文件扩展名的更改
    if 和 elif 的区别
  • 原文地址:https://www.cnblogs.com/muzisanshi/p/13041507.html
Copyright © 2011-2022 走看看