优化案例3:高频多union表关联穿插拼接完成计数


DM技术交流QQ群:940124259

1. 引入

已经很久没有发布文章,快沉底!赶紧拿近期优化的案例分享一下,本次分享的主要知识是快速定位SQL瓶颈点以及SQL分析函数的等价改写。
本例SQL代码比较长(几百行),特征是大量的多表外关联,穿插几处union all拼接结果集(集合操作),其中一些不重要的部分会一笔带过,着重讲解关键点。


2. 解决过程

2.1 瓶颈定位思想

遇到特别长的SQL,想快速找出瓶颈位置,就要充分掌握拆分思想,即一段一段拆分出符合语法语义的子SQL尝试执行,找出性能最差的子SQL,再花精力攻克它。

比如本案例的特征union all多处拼接,可以以union all作为划分界限,逐步跟踪子语句性能。

-- 经脱敏处理的完整SQL代码
select count(1) 
  from ( SELECT XMXX.IS_LOW_RISK, 
                 XMXX.ID, 
                 XMXX.REPORT_TYPE, 
                 XMXX.TYPE, 
                 XMXX.TYPE AS STAGE, 
                 XMXX.STATUS, 
                 XMXX.CODE, 
                 XMXX.NAME, 
                 XMXX.OLD_ID, 
                 XMXX.CUR_DEPARTMENT_GUID, 
                 XMXX.CUR_OFFICE_GUID, 
                 XMXX.CUR_USER_ID, 
                 XMXX.ATTEND_STATUS, 
                 XMXX.APPLY_TIME, 
                 XMXX.APPLY_TIME as update_time, 
                 PRO_COUNTY.COUNTY, 
                 PRO_JSXZ.JSXZ, 
                 XMXX.SYS_FLAG, 
                 XMXX.VERSION, 
                 XMXX.TOTAL_INVEST, 
                 DE.department_fullname AS approve_dept_name, 
                 DE.parent_guid, 
                 DE.store_level, 
                 OWN.TYPE OWN_TYPE, 
                 XMXX.LEGAL, 
                 OWN.COMPANY_NAME COMPANY_NAME, 
                 '0' as xmflag , 
                 XMXX.is_ppp_item 
            FROM SHB_CQCQ XMXX 
       LEFT JOIN SHB_USER_INFO OWN 
              ON OWN.USER_ID = XMXX.CREATE_USER_ID 
       LEFT JOIN( SELECT ITEM_FULL_VALUE COUNTY, 
                         ITEM_KEY 
                    FROM DICTIONARY_ITEMS 
                   WHERE GROUP_NO = '1008') PRO_COUNTY 
              ON PRO_COUNTY.ITEM_KEY = XMXX.COUNTY 
       LEFT JOIN( SELECT ITEM_FULL_VALUE JSXZ, 
                         ITEM_KEY 
                    FROM DICTIONARY_ITEMS 
                   WHERE GROUP_NO = '1009') PRO_JSXZ 
              ON PRO_JSXZ.ITEM_KEY = XMXX.JSXZ 
       LEFT JOIN DEPARTMENT DE 
              ON XMXX.duty_department_id = DE.DEPARTMENT_GUID 
       LEFT JOIN (SELECT MAX(CUR_DEAL_TIME) AS CUR_DEAL_TIME, 
                          RELATE_BILL_ID 
                     FROM SHB_TO_DEAL_LOG 
                    WHERE TYPE = 'SHB-OPT-08' 
                 GROUP BY RELATE_BILL_ID) TD 
              ON TD.RELATE_BILL_ID = XMXX.ID 
           WHERE XMXX.IS_DEL = '0' 
             AND (XMXX.REPORT_TYPE NOT IN('3', 
                                          '2') 
                        OR (XMXX.REPORT_TYPE IN('3', 
                                                '2') 
                                 and (XMXX.ATTEND_STATUS <>50 
                                            or XMXX.ATTEND_STATUS IS NULL))) 
             AND IS_SHOW = 0 
             AND XMXX.COUNTY in ('CQ50011001', 
                                 'CQ50011002') 
             AND XMXX.status not in ('10') 
             AND ( XMXX.NAME LIKE '%重庆重庆%' 
                        OR XMXX.LEGAL LIKE '%重庆重庆%' 
                        OR XMXX.CODE LIKE '%重庆重庆%') 
       Union All 
          SELECT '0' as IS_LOW_RISK, 
                 XMXX.ID, 
                 XMXX.REPORT_TYPE, 
                 XMXX.TYPE, 
                 XMXX.TYPE AS STAGE, 
                 XMXX.STATUS, 
                 XMXX.CODE, 
                 XMXX.NAME, 
                 XMXX.OLD_ID, 
                 XMXX.CUR_DEPARTMENT_GUID, 
                 XMXX.CUR_OFFICE_GUID, 
                 XMXX.CUR_USER_ID, 
                 XMXX.ATTEND_STATUS, 
                 XMXX.APPLY_TIME, 
                 XMXX.APPLY_TIME as update_time, 
                 PRO_COUNTY.COUNTY, 
                 PRO_JSXZ.JSXZ, 
                 XMXX.SYS_FLAG, 
                 XMXX.VERSION, 
                 XMXX.TOTAL_INVEST, 
                 DE.department_fullname AS approve_dept_name, 
                 DE.parent_guid, 
                 DE.store_level, 
                 OWN.TYPE OWN_TYPE, 
                 XMXX.LEGAL, 
                 OWN.COMPANY_NAME COMPANY_NAME, 
                 '1' as xmflag, 
                 '0' as is_ppp_item 
            FROM SHB_CQCQ_TAGGING XMXX 
       LEFT JOIN SHB_USER_INFO OWN 
              ON OWN.USER_ID = XMXX.CREATE_USER_ID 
       LEFT JOIN( SELECT ITEM_FULL_VALUE COUNTY, 
                         ITEM_KEY 
                    FROM DICTIONARY_ITEMS 
                   WHERE GROUP_NO = '1008') PRO_COUNTY 
              ON PRO_COUNTY.ITEM_KEY = XMXX.COUNTY 
       LEFT JOIN( SELECT ITEM_FULL_VALUE JSXZ, 
                         ITEM_KEY 
                    FROM DICTIONARY_ITEMS 
                   WHERE GROUP_NO = '1009') PRO_JSXZ 
              ON PRO_JSXZ.ITEM_KEY = XMXX.JSXZ 
       LEFT JOIN DEPARTMENT DE 
              ON XMXX.duty_department_id = DE.DEPARTMENT_GUID 
       LEFT JOIN (SELECT MAX(CUR_DEAL_TIME) AS CUR_DEAL_TIME, 
                          RELATE_BILL_ID 
                     FROM SHB_TO_DEAL_LOG 
                    WHERE TYPE = 'SHB-OPT-08' 
                 GROUP BY RELATE_BILL_ID) TD 
              ON TD.RELATE_BILL_ID = XMXX.ID 
           WHERE XMXX.IS_DEL = '0' 
             AND (XMXX.REPORT_TYPE NOT IN('3', 
                                          '2') 
                        OR (XMXX.REPORT_TYPE IN('3', 
                                                '2') 
                                 and (XMXX.ATTEND_STATUS <>50 
                                            or XMXX.ATTEND_STATUS IS NULL))) 
             AND IS_SHOW = 0 
             AND XMXX.COUNTY in ('CQ50011001', 
                                 'CQ50011002') 
             AND XMXX.status not in ('10') 
             AND ( XMXX.NAME LIKE '%重庆重庆%' 
                        OR XMXX.LEGAL LIKE '%重庆重庆%' 
                        OR XMXX.CODE LIKE '%重庆重庆%') 
       Union All 
          SELECT XMXX.IS_LOW_RISK, 
                 px.ID, 
                 XMXX.REPORT_TYPE, 
                 XMXX.TYPE, 
                 XMXX.TYPE AS STAGE, 
                 XMXX.STATUS, 
                 XMXX.CODE, 
                 XMXX.NAME, 
                 px.SHB_CQCQ_id as OLD_ID, 
                 XMXX.CUR_DEPARTMENT_GUID, 
                 XMXX.CUR_OFFICE_GUID, 
                 XMXX.CUR_USER_ID, 
                 XMXX.ATTEND_STATUS, 
                 XMXX.APPLY_TIME, 
                 XMXX.update_time, 
                 PRO_COUNTY.COUNTY, 
                 PRO_JSXZ.JSXZ, 
                 XMXX.SYS_FLAG, 
                 XMXX.VERSION, 
                 XMXX.TOTAL_INVEST, 
                 DE.department_fullname AS approve_dept_name, 
                 DE.parent_guid, 
                 DE.store_level, 
                 OWN.TYPE OWN_TYPE, 
                 XMXX.LEGAL, 
                 px.ppp_item_stage COMPANY_NAME, 
                 '2' as xmflag , 
                 XMXX.is_ppp_item 
            from SHB_CQCQ_ppp px 
            JOIN SHB_CQCQ XMXX 
              ON px.SHB_CQCQ_id=XMXX.id 
       LEFT JOIN SHB_USER_INFO OWN 
              ON OWN.USER_ID = XMXX.CREATE_USER_ID 
       LEFT JOIN( SELECT ITEM_FULL_VALUE COUNTY, 
                         ITEM_KEY 
                    FROM DICTIONARY_ITEMS 
                   WHERE GROUP_NO = '1008') PRO_COUNTY 
              ON PRO_COUNTY.ITEM_KEY = XMXX.COUNTY 
       LEFT JOIN( SELECT ITEM_FULL_VALUE JSXZ, 
                         ITEM_KEY 
                    FROM DICTIONARY_ITEMS 
                   WHERE GROUP_NO = '1009') PRO_JSXZ 
              ON PRO_JSXZ.ITEM_KEY = XMXX.JSXZ 
       LEFT JOIN DEPARTMENT DE 
              ON XMXX.duty_department_id = DE.DEPARTMENT_GUID 
           where 1=1 
             AND XMXX.COUNTY in ('CQ50011001', 
                                 'CQ50011002') 
             AND ( XMXX.NAME LIKE '%重庆重庆%' 
                        OR XMXX.LEGAL LIKE '%重庆重庆%' 
                        OR XMXX.CODE LIKE '%重庆重庆%') 
             AND px.pp_status in('33', 
                                 '50', 
                                 '40', 
                                 '12', 
                                 '41', 
                                 '71') 
       Union All 
       select E.* 
         from ( select distinct '0' AS IS_LOW_RISK, 
                        F.id, 
                        to_number('999') AS report_type, 
                        F.pro_type as type, 
                        F.apply_stage as stage, 
                        to_number(F.status) as status, 
                        F.gj_pro_code AS code, 
                        F.gj_pro_name AS name, 
                        '' as old_id, 
                        'CHONGQI-3' as CUR_DEPARTMENT_GUID, 
                        'CHONGQI-3' as CUR_OFFICE_GUID, 
                        '' as CUR_USER_ID, 
                        F.matters as attend_status, 
                        F.update_time as apply_time, 
                        F.update_time, 
                        pro_county.county, 
                        pro_jsxz.item_full_value AS jsxz, 
                        to_number('999') AS sys_flag, 
                        to_number('0') AS version, 
                        F.gj_investment_total as TOTAL_INVEST, 
                        '顶呱呱' as approve_dept_name, 
                        '' as parent_guid, 
                        to_number('000') as store_level, 
                        own.type AS own_type, 
                        F.gj_pro_org as legal, 
                        own.company_name, 
                        F.matters as xmflag, 
                        '' as is_ppp_item 
                   from ( select xmxx.total_invest as gj_investment_total, 
                                  pro.build_properties, 
                                  pro.gj_pro_org, 
                                  C.matters, 
                                  C.apply_stage, 
                                  C.pro_type, 
                                  C.id, 
                                  xmxx.code as gj_pro_code, 
                                  xmxx.name as gj_pro_name, 
                                  C.create_user_id, 
                                  pro.county, 
                                  C.apply_matters, 
                                  xmxx.status, 
                                  C.update_time 
                             from ( select 'inout_project' as matters, 
                                          A.apply_stage, 
                                          A.pro_type, 
                                          A.id, 
                                          A.project_id, 
                                          A.update_time, 
                                          A.create_user_id, 
                                          A.apply_matters 
                                     from service_inout_project A
                                    where A.is_del = '0' 
                                      and A.status in('01', 
                                                      '02', 
                                                      '11', 
                                                      '13', 
                                                      '21', 
                                                      '23', 
                                                      '12', 
                                                      '22', 
                                                      '15', 
                                                      '16', 
                                                      '17', 
                                                      '18', 
                                                      '25', 
                                                      '26', 
                                                      '27', 
                                                      '28', 
                                                      '14', 
                                                      '24')
                                  union all
                                  select 'free_project' as matters, 
                                         B.apply_stage, 
                                         B.pro_type, 
                                         B.id, 
                                         B.project_id, 
                                         B.update_time, 
                                         B.create_user_id, 
                                         B.apply_matters 
                                    from service_free_project B
                                   where B.is_del = '0' 
                                     and B.status in('01', 
                                                     '02', 
                                                     '11', 
                                                     '13', 
                                                     '21', 
                                                     '23', 
                                                     '12', 
                                                     '22', 
                                                     '15', 
                                                     '16', 
                                                     '17', 
                                                     '18', 
                                                     '25', 
                                                     '26', 
                                                     '27', 
                                                     '28', 
                                                     '14', 
                                                     '24')) C
                        left join cq_base_pro pro 
                               on pro.id = C.project_id 
                        left join SHB_CQCQ xmxx 
                               on xmxx.cq_base_pro_id = pro.id 
                              and xmxx.is_del = '0'
                        union all
                           select info.total_money as gj_investment_total, 
                                  '' as build_properties, 
                                  '' as gj_pro_org, 
                                  E.matters, 
                                  E.apply_stage, 
                                  E.pro_type, 
                                  E.id, 
                                  info.project_code as gj_pro_code, 
                                  info.project_name as gj_pro_name, 
                                  E.create_user_id, 
                                  info.place_code as county, 
                                  E.apply_matters, 
                                  '' as status, 
                                  E.update_time 
                             from ( select 'free_project' as matters, 
                                          D.apply_stage, 
                                          D.pro_type, 
                                          D.id, 
                                          D.project_id, 
                                          D.update_time, 
                                          D.create_user_id, 
                                          D.apply_matters, 
                                          D.status 
                                     from service_free_project D 
                                    where D.is_del = '0' 
                                      and D.status in('01', 
                                                      '02', 
                                                      '11', 
                                                      '13', 
                                                      '21', 
                                                      '23', 
                                                      '12', 
                                                      '22', 
                                                      '15', 
                                                      '16', 
                                                      '17', 
                                                      '18', 
                                                      '25', 
                                                      '26', 
                                                      '27', 
                                                      '28', 
                                                      '14', 
                                                      '24')) E
                        left join region_apply_pro_info info 
                               on E.project_id = info.id ) F
              left join shb_user_info own 
                     on own.user_id = F.create_user_id
              left join (select item_full_value county, 
                                item_key 
                           from dictionary_items 
                          where group_no = '1008') pro_county
                     on pro_county.item_key = F.county
              left join ( select max(cur_deal_time) as cur_deal, 
                                 relate_bill_id
                            from shb_to_deal_log 
                           where cur_department_guid = 'CHONGQI-31' 
                        group by relate_bill_id ) td
                     on td.relate_bill_id = F.id 
              left join (select item_full_value, 
                                item_key 
                           from dictionary_items 
                          where group_no = '1009') pro_jsxz 
                     on pro_jsxz.item_key = F.build_properties 
                  where F.gj_pro_code is not null 
              union all 
                 select  distinct '' as is_low_risk , 
                        ch.id , 
                        xmxx.report_type , 
                        '8' as type , 
                        ch.apply_stage as stage , 
                        ch.status , 
                        ch.pro_code as code , 
                        ch.pro_name as name , 
                        xmxx.old_id , 
                        XMXX.CUR_DEPARTMENT_GUID , 
                        XMXX.CUR_OFFICE_GUID , 
                        XMXX.CUR_USER_ID , 
                        XMXX.ATTEND_STATUS , 
                        XMXX.APPLY_TIME, 
                        ch.update_time , 
                        pro_county.county , 
                        PRO_JSXZ.item_full_value as JSXZ , 
                        XMXX.SYS_FLAG , 
                        XMXX.VERSION , 
                        XMXX.TOTAL_INVEST, 
                        de.department_fullname as approve_dept_name, 
                        de.parent_guid , 
                        de.store_level , 
                        own.type as own_type , 
                        xmxx.legal , 
                        OWN.COMPANY_NAME COMPANY_NAME , 
                        'charter' as xmflag , 
                        xmxx.is_ppp_item 
                   from charter_project ch 
              left join cq_base_pro b 
                     on b.id = ch.cq_base_id 
              left join SHB_CQCQ xmxx 
                     on xmxx.cq_base_pro_id = ch.cq_base_id 
                    and xmxx.is_del = '0' 
              left join ( select item_full_value county, 
                                item_key 
                           from dictionary_items 
                          where group_no = '1008' ) pro_county 
                     on pro_county.item_key = xmxx.county 
              left join ( select item_full_value, 
                                item_key 
                           from dictionary_items 
                          where group_no = '1009' ) pro_jsxz 
                     on pro_jsxz.item_key = xmxx.jsxz 
              LEFT JOIN DEPARTMENT DE 
                     ON XMXX.duty_department_id = DE.DEPARTMENT_GUID 
              LEFT JOIN SHB_USER_INFO OWN 
                     ON OWN.USER_ID = XMXX.CREATE_USER_ID 
                  WHERE XMXX.COUNTY in ('CQ50011001', 
                                        'CQ50011002') 
                     or XMXX.COUNTY IS NULL 
              union all 
                 SELECT XMXX.IS_LOW_RISK ,
                        en.id ,
                        XMXX.REPORT_TYPE ,
                        XMXX.TYPE ,
                        en.apply_stage AS STAGE ,
                        en.STATUS ,
                        XMXX.CODE ,
                        XMXX.NAME ,
                        XMXX.OLD_ID ,
                        XMXX.CUR_DEPARTMENT_GUID ,
                        XMXX.CUR_OFFICE_GUID ,
                        XMXX.CUR_USER_ID ,
                        XMXX.ATTEND_STATUS ,
                        XMXX.APPLY_TIME ,
                        en.update_time ,
                        PRO_COUNTY.COUNTY ,
                        PRO_JSXZ.item_full_value as JSXZ ,
                        XMXX.SYS_FLAG ,
                        XMXX.VERSION ,
                        XMXX.TOTAL_INVEST ,
                        case when en.reply_level = '0' then '教室' else DE.department_fullname end AS approve_dept_name,
                        DE.parent_guid ,
                        DE.store_level ,
                        OWN.TYPE OWN_TYPE ,
                        XMXX.LEGAL ,
                        OWN.COMPANY_NAME COMPANY_NAME ,
                        'energy' as xmflag ,
                        XMXX.is_ppp_item
                   from energy_audit en
              left join ( select x.*
                              from SHB_CQCQ x
                        inner join ( select code, 
                                            max(create_time) utime 
                                       from SHB_CQCQ 
                                      where (status = '40' or type = '5') 
                                   group by code ) xx
                                on xx.code = x.code and x.create_time = xx.utime ) xmxx 
                     on xmxx.code = en.project_code
              left join ( select item_full_value county,
                                item_key
                           from dictionary_items
                          where group_no = '1008' ) pro_county
                     on pro_county.item_key = xmxx.county
              left join ( select item_full_value,
                                item_key
                           from dictionary_items
                          where group_no = '1009' ) pro_jsxz
                     on pro_jsxz.item_key = xmxx.jsxz
              LEFT JOIN DEPARTMENT DE
                     ON XMXX.duty_department_id = DE.DEPARTMENT_GUID
              LEFT JOIN SHB_USER_INFO OWN
                     ON OWN.USER_ID = XMXX.CREATE_USER_ID 
                  where 1=1 AND XMXX.COUNTY in ('CQ50011001', 
                                                'CQ50011002') ) E 
        where 1=1 AND ( E.name LIKE '%重庆重庆%' OR E.code LIKE '%重庆重庆%') ) TOTAL

2.2 定位解决过程

针对union all分支一个一个从上到下尝试执行,记录耗时,很快地追踪到有两处位置性能特别差。

2.2.1 子部分代码1

-- 脱敏SQL
select xmxx.total_invest as gj_investment_total, 
                                  pro.build_properties, 
                                  pro.gj_pro_org, 
                                  C.matters, 
                                  C.apply_stage, 
                                  C.pro_type, 
                                  C.id, 
                                  xmxx.code as gj_pro_code, 
                                  xmxx.name as gj_pro_name, 
                                  C.create_user_id, 
                                  pro.county, 
                                  C.apply_matters, 
                                  xmxx.status, 
                                  C.update_time 
                             from ( select 'inout_project' as matters, 
                                          A.apply_stage, 
                                          A.pro_type, 
                                          A.id, 
                                          A.project_id, 
                                          A.update_time, 
                                          A.create_user_id, 
                                          A.apply_matters 
                                     from service_inout_project A
                                    where A.is_del = '0' 
                                      and A.status in('01', 
                                                      '02', 
                                                      '11', 
                                                      '13', 
                                                      '21', 
                                                      '23', 
                                                      '12', 
                                                      '22', 
                                                      '15', 
                                                      '16', 
                                                      '17', 
                                                      '18', 
                                                      '25', 
                                                      '26', 
                                                      '27', 
                                                      '28', 
                                                      '14', 
                                                      '24')
                                  union all
                                  select 'free_project' as matters, 
                                         B.apply_stage, 
                                         B.pro_type, 
                                         B.id, 
                                         B.project_id, 
                                         B.update_time, 
                                         B.create_user_id, 
                                         B.apply_matters 
                                    from service_free_project B
                                   where B.is_del = '0' 
                                     and B.status in('01', 
                                                     '02', 
                                                     '11', 
                                                     '13', 
                                                     '21', 
                                                     '23', 
                                                     '12', 
                                                     '22', 
                                                     '15', 
                                                     '16', 
                                                     '17', 
                                                     '18', 
                                                     '25', 
                                                     '26', 
                                                     '27', 
                                                     '28', 
                                                     '14', 
                                                     '24')) C
                        left join cq_base_pro pro 
                               on pro.id = C.project_id 
                        left join SHB_CQCQ xmxx 
                               on xmxx.cq_base_pro_id = pro.id 
                              and xmxx.is_del = '0'                                                                      

代码1执行计划:一看执行计划步骤20就怀疑有问题,SHB_CQCQ AS
XMXX表大小567MB,表记录1123476,整体查询结果集返回2条。检查步骤4-18真实返回少,步骤19-20应该补建索引,根据步骤3计划得知关联字段XMXX.CQ_BASE_PRO_ID,则对它建一个单列索引。

-- 优化前执行计划
1   #NSET2: [79, 2, 648] 
2     #PRJT2: [79, 2, 648]; exp_num(14), is_atom(FALSE) 
3       #HASH LEFT JOIN2: [79, 2, 648]; key_num(1), partition_keys_num(0)  KEY(PRO.ID=XMXX.CQ_BASE_PRO_ID)
4         #INDEX JOIN LEFT JOIN2: [2, 2, 396] 
5           #PRJT2: [1, 2, 396]; exp_num(8), is_atom(FALSE) 
6             #UNION ALL: [1, 2, 396] 
7               #PRJT2: [0, 1, 396]; exp_num(8), is_atom(FALSE) 
8                 #HASH LEFT SEMI JOIN2: [0, 1, 396]; KEY_NUM(1);  KEY(A.STATUS=DMTEMPVIEW_17674615.colname) KEY_NULL_EQU(0)
9                   #SLCT2: [0, 1, 396]; A.IS_DEL = '0'
10                    #CSCN2: [0, 7, 396]; INDEX33559165(SERVICE_INOUT_PROJECT as A)
11                  #CONST VALUE LIST: [0, 18, 48]; row_num(18), col_num(1), 
12              #PRJT2: [0, 1, 396]; exp_num(8), is_atom(FALSE) 
13                #HASH LEFT SEMI JOIN2: [0, 1, 396]; KEY_NUM(1);  KEY(B.STATUS=DMTEMPVIEW_17674616.colname) KEY_NULL_EQU(0)
14                  #SLCT2: [0, 1, 396]; B.IS_DEL = '0'
15                    #CSCN2: [0, 1, 396]; INDEX33559166(SERVICE_FREE_PROJECT as B)
16                  #CONST VALUE LIST: [0, 18, 48]; row_num(18), col_num(1), 
17          #BLKUP2: [0, 1, 0]; INDEX33559875(PRO)
18            #SSEK2: [0, 1, 0]; scan_type(ASC), INDEX33559875(CQ_BASE_PRO as PRO), scan_range[C.PROJECT_ID,C.PROJECT_ID]
19        #SLCT2: [51, 256955, 252]; XMXX.IS_DEL = '0'
20          #CSCN2: [51, 311391, 252]; INDEX33559137(SHB_CQCQ as XMXX)   ************ 性能瓶颈点 ************

处理方法 :补建索引
create index idxfs_shb_CQCQ_cq_base_pro_id on cq.shb_CQCQ(cq_base_pro_id);

-- 优化后执行计划
1   #NSET2: [2, 9, 396] 
2     #PRJT2: [2, 9, 396]; exp_num(14), is_atom(FALSE) 
3       #INDEX JOIN LEFT JOIN2: [2, 9, 396] join condition(XMXX.IS_DEL = '0')
4         #INDEX JOIN LEFT JOIN2: [2, 2, 396] 
5           #PRJT2: [1, 2, 396]; exp_num(8), is_atom(FALSE) 
6             #UNION ALL: [1, 2, 396] 
7               #PRJT2: [0, 1, 396]; exp_num(8), is_atom(FALSE) 
8                 #HASH LEFT SEMI JOIN2: [0, 1, 396]; KEY_NUM(1);  KEY(A.STATUS=DMTEMPVIEW_17681581.colname) KEY_NULL_EQU(0)
9                   #SLCT2: [0, 1, 396]; A.IS_DEL = '0'
10                    #CSCN2: [0, 7, 396]; INDEX33559165(SERVICE_INOUT_PROJECT as A)
11                  #CONST VALUE LIST: [0, 18, 48]; row_num(18), col_num(1), 
12              #PRJT2: [0, 1, 396]; exp_num(8), is_atom(FALSE) 
13                #HASH LEFT SEMI JOIN2: [0, 1, 396]; KEY_NUM(1);  KEY(B.STATUS=DMTEMPVIEW_17681582.colname) KEY_NULL_EQU(0)
14                  #SLCT2: [0, 1, 396]; B.IS_DEL = '0'
15                    #CSCN2: [0, 1, 396]; INDEX33559166(SERVICE_FREE_PROJECT as B)
16                  #CONST VALUE LIST: [0, 18, 48]; row_num(18), col_num(1), 
17          #BLKUP2: [0, 1, 0]; INDEX33559875(PRO)
18            #SSEK2: [0, 1, 0]; scan_type(ASC), INDEX33559875(CQ_BASE_PRO as PRO), scan_range[C.PROJECT_ID,C.PROJECT_ID]
19        #BLKUP2: [0, 1, 0]; IDXFS_SHB_CQCQ_CQ_BASE_PRO_ID(XMXX)
20          #SSEK2: [0, 1, 0]; scan_type(ASC), IDXFS_SHB_CQCQ_CQ_BASE_PRO_ID(SHB_CQCQ as XMXX), scan_range[PRO.ID,PRO.ID]   ******* 优化效果点  *******

2.2.2 子部分代码2

-- 脱敏SQL
   SELECT XMXX.IS_LOW_RISK ,
                        en.id ,
                        XMXX.REPORT_TYPE ,
                        XMXX.TYPE ,
                        en.apply_stage AS STAGE ,
                        en.STATUS ,
                        XMXX.CODE ,
                        XMXX.NAME ,
                        XMXX.OLD_ID ,
                        XMXX.CUR_DEPARTMENT_GUID ,
                        XMXX.CUR_OFFICE_GUID ,
                        XMXX.CUR_USER_ID ,
                        XMXX.ATTEND_STATUS ,
                        XMXX.APPLY_TIME ,
                        en.update_time ,
                        PRO_COUNTY.COUNTY ,
                        PRO_JSXZ.item_full_value as JSXZ ,
                        XMXX.SYS_FLAG ,
                        XMXX.VERSION ,
                        XMXX.TOTAL_INVEST ,
                        case when en.reply_level = '0' then '教室' else DE.department_fullname end AS approve_dept_name,
                        DE.parent_guid ,
                        DE.store_level ,
                        OWN.TYPE OWN_TYPE ,
                        XMXX.LEGAL ,
                        OWN.COMPANY_NAME COMPANY_NAME ,
                        'energy' as xmflag ,
                        XMXX.is_ppp_item
                   from energy_audit en
              left join ( select x.*
                              from SHB_CQCQ x
                        inner join (select code, 
                                            max(create_time) utime 
                                       from SHB_CQCQ 
                                      where (status = '40' or type = '5') 
                                   group by code ) xx
                                on xx.code = x.code and x.create_time = xx.utime ) xmxx 
                     on xmxx.code = en.project_code
              left join ( select item_full_value county,
                                item_key
                           from dictionary_items
                          where group_no = '1008' ) pro_county
                     on pro_county.item_key = xmxx.county
              left join ( select item_full_value,
                                item_key
                           from dictionary_items
                          where group_no = '1009' ) pro_jsxz
                     on pro_jsxz.item_key = xmxx.jsxz
              LEFT JOIN DEPARTMENT DE
                     ON XMXX.duty_department_id = DE.DEPARTMENT_GUID
              LEFT JOIN SHB_USER_INFO OWN
                     ON OWN.USER_ID = XMXX.CREATE_USER_ID 
                  where 1=1 AND XMXX.COUNTY in ('CQ50011001', 
                                                'CQ50011002')   /*耗时985毫秒*/ 

子代码2的执行计划:看来SHB_CQCQ表属于业务核心表,哪哪都有它,反复的引用,体积有点大。
迅速怀疑(status = ‘40’ or type = ‘5’) 过滤条件很差,而优化器默认union分治再合并处理(步骤21-25),赶紧过滤查查select count(*) from shb_xmxx XMXX where (status = ‘40’ or type = ‘5’) ; – 235 931,返回的结果集比较大啦。
显然步骤21-25执行计划是错误的,应该考虑过滤条件合体,而不是拆分再合并(搞出步骤22的错误索引回表和步骤25的二次全表扫描),这个可以用hint optimizer_or_nbexp(2)解决。
再继续针对XMXX内联视图的查看,SHB_CQCQ as X又被执行了一次自关联,执行计划步骤26体现,显然这种不科学。他的初衷大致取每个项目最近的处理时间,完全没必要反复自关联这么搞,弄成分析函数作辅助列再过滤即可,减少一次SHB_CQCQ as X表的访问,性能自然而然地提高。

-- 未优化前执行计划
1   #NSET2: [1196, 7949, 7886] 
2     #PRJT2: [1196, 7949, 7886]; exp_num(28), is_atom(FALSE) 
3       #INDEX JOIN LEFT JOIN2: [1196, 7949, 7886] 
4         #INDEX JOIN LEFT JOIN2: [1187, 1295, 7886] 
5           #HASH RIGHT JOIN2: [1178, 1295, 7886]; key_num(1),  KEY(PRO_JSXZ.ITEM_KEY=XMXX.JSXZ)
6             #PRJT2: [1, 8, 144]; exp_num(2), is_atom(FALSE) 
7               #SLCT2: [1, 8, 144]; DICTIONARY_ITEMS.GROUP_NO = '1009'
8                 #CSCN2: [1, 9158, 144]; INDEX33559353(DICTIONARY_ITEMS)
9             #HASH RIGHT JOIN2: [1175, 1295, 7742]; key_num(1),  KEY(PRO_COUNTY.ITEM_KEY=XMXX.COUNTY)
10              #PRJT2: [1, 72, 144]; exp_num(2), is_atom(FALSE) 
11                #SLCT2: [1, 72, 144]; DICTIONARY_ITEMS.GROUP_NO = '1008'
12                  #CSCN2: [1, 9158, 144]; INDEX33559353(DICTIONARY_ITEMS)
13              #HASH2 INNER JOIN: [1171, 1295, 7598];  KEY_NUM(1); KEY(EN.PROJECT_CODE=XMXX.CODE) KEY_NULL_EQU(0)
14                #CSCN2: [0, 1328, 252]; INDEX33560566(ENERGY_AUDIT as EN)
15                #HASH RIGHT SEMI JOIN2: [1162, 7727, 7346]; n_keys(1)   KEY(DMTEMPVIEW_17697425.colname=XMXX.COUNTY) KEY_NULL_EQU(0)
16                  #CONST VALUE LIST: [0, 2, 48]; row_num(2), col_num(1), 
17                  #PRJT2: [1157, 154543, 7346]; exp_num(20), is_atom(FALSE) 
18                    #HASH2 INNER JOIN: [1157, 154543, 7346];  KEY_NUM(2); KEY(XX.CODE=X.CODE AND XX.UTIME=X.CREATE_TIME) KEY_NULL_EQU(0, 0)
19                      #PRJT2: [108, 227506, 146]; exp_num(2), is_atom(FALSE) 
20                        #HAGR2: [108, 227506, 146]; grp_num(1), sfun_num(1); slave_empty(0) keys(SHB_CQCQ.CODE) 
21                          #UNION FOR OR2: [86, 263623, 146]; key_num(1)     ************ 性能瓶颈点 ************
22                            #BLKUP2: [41, 36060, 146]; INDEX_NAME_TYPE(SHB_CQCQ)
23                              #SSEK2: [41, 36060, 146]; scan_type(ASC), INDEX_NAME_TYPE(SHB_CQCQ), scan_range['5','5']
24                            #SLCT2: [45, 227562, 146]; SHB_CQCQ.STATUS = var2
25                              #CSCN2: [45, 311391, 146]; INDEX33559137(SHB_CQCQ) 
26                      #CSCN2: [712, 311391, 7200]; INDEX33559137(SHB_CQCQ as X)   ************ 性能瓶颈点 ************
27          #BLKUP2: [8, 1, 0]; INDEX33559823(DE)
28            #SSEK2: [8, 1, 0]; scan_type(ASC), INDEX33559823(DEPARTMENT as DE), scan_range[XMXX.DUTY_DEPARTMENT_ID,XMXX.DUTY_DEPARTMENT_ID]
29        #BLKUP2: [9, 7, 0]; SHB_USER_INFO_USERID_INDEX(OWN)
30          #SSEK2: [9, 7, 0]; scan_type(ASC), SHB_USER_INFO_USERID_INDEX(SHB_USER_INFO as OWN), scan_range[XMXX.CREATE_USER_ID,XMXX.CREATE_USER_ID]

处理方法:分析函数等价改写和HINT处理OR不展开

-- 等价改写和HINT指定
     SELECT XMXX.IS_LOW_RISK ,
                        en.id ,
                        XMXX.REPORT_TYPE ,
                        XMXX.TYPE ,
                        en.apply_stage AS STAGE ,
                        en.STATUS ,
                        XMXX.CODE ,
                        XMXX.NAME ,
                        XMXX.OLD_ID ,
                        XMXX.CUR_DEPARTMENT_GUID ,
                        XMXX.CUR_OFFICE_GUID ,
                        XMXX.CUR_USER_ID ,
                        XMXX.ATTEND_STATUS ,
                        XMXX.APPLY_TIME ,
                        en.update_time ,
                        PRO_COUNTY.COUNTY ,
                        PRO_JSXZ.item_full_value as JSXZ ,
                        XMXX.SYS_FLAG ,
                        XMXX.VERSION ,
                        XMXX.TOTAL_INVEST ,
                        case when en.reply_level = '0' then '教室' else DE.department_fullname end AS approve_dept_name,
                        DE.parent_guid ,
                        DE.store_level ,
                        OWN.TYPE OWN_TYPE ,
                        XMXX.LEGAL ,
                        OWN.COMPANY_NAME COMPANY_NAME ,
                        'energy' as xmflag ,
                        XMXX.is_ppp_item
    from energy_audit en
              left join ( select /*+ optimizer_or_nbexp(2)*/xx.*
                            from (
                                select x.*, max(create_time) over(partition by code ) utime
                                from SHB_CQCQ x
                                where (status = '40' or type = '5') 
                            ) xx
                               where xx.utime = create_time
                        ) xmxx 
                     on xmxx.code = en.project_code
              left join ( select item_full_value county,
                                item_key
                           from dictionary_items
                          where group_no = '1008' ) pro_county
                     on pro_county.item_key = xmxx.county
              left join ( select item_full_value,
                                item_key
                           from dictionary_items
                          where group_no = '1009' ) pro_jsxz
                     on pro_jsxz.item_key = xmxx.jsxz
              LEFT JOIN DEPARTMENT DE
                     ON XMXX.duty_department_id = DE.DEPARTMENT_GUID
              LEFT JOIN SHB_USER_INFO OWN
                     ON OWN.USER_ID = XMXX.CREATE_USER_ID 
                  where 1=1 AND XMXX.COUNTY in ('CQ50011001', 
                                                'CQ50011002')

在这里插入图片描述

-- 优化后执行计划
1   #NSET2: [241, 400, 7740] 
2     #PRJT2: [241, 400, 7740]; exp_num(28), is_atom(FALSE) 
3       #INDEX JOIN LEFT JOIN2: [241, 400, 7740] 
4         #INDEX JOIN LEFT JOIN2: [236, 65, 7740] 
5           #HASH RIGHT JOIN2: [232, 65, 7740]; key_num(1),  KEY(PRO_JSXZ.ITEM_KEY=XMXX.JSXZ)
6             #PRJT2: [0, 8, 144]; exp_num(2), is_atom(FALSE) 
7               #BLKUP2: [0, 8, 144]; IDXFS_DICTIONARY_ITEMS_GROUP_NO(DICTIONARY_ITEMS)
8                 #SSEK2: [0, 8, 144]; scan_type(ASC), IDXFS_DICTIONARY_ITEMS_GROUP_NO(DICTIONARY_ITEMS), scan_range['1009','1009']
9             #HASH LEFT JOIN2: [231, 65, 7596]; key_num(1), partition_keys_num(0)  KEY(XMXX.COUNTY=PRO_COUNTY.ITEM_KEY)
10              #HASH RIGHT SEMI JOIN2: [230, 65, 7452]; n_keys(1)   KEY(DMTEMPVIEW_17743948.colname=XMXX.COUNTY) KEY_NULL_EQU(0)
11                #CONST VALUE LIST: [0, 2, 48]; row_num(2), col_num(1), 
12                #SLCT2: [230, 65, 7452]; XMXX.CODE = EN.PROJECT_CODE
13                  #NEST LOOP INNER JOIN2: [230, 65, 7452]; [with var]
14                    #CSCN2: [0, 1328, 252]; INDEX33560566(ENERGY_AUDIT as EN)
15                    #PRJT2: [0, 1, 7200]; exp_num(20), is_atom(FALSE) 
16                      #SLCT2: [0, 1, 7200]; XX.UTIME = XX.CREATE_TIME  ******* 优化效果点  *******
17                        #PRJT2: [0, 1, 7200]; exp_num(22), is_atom(FALSE) 
18                          #AFUN: [0, 1, 7200]; afun_num(1); partition_num(1)[X.CODE]; order_num(0)  
19                            #SLCT2: [0, 1, 7200]; (X.TYPE = '5' OR X.STATUS = var3)
20                              #BLKUP2: [0, 1, 7200]; SHB_CQCQ_CODE_INDEX(X)
21                                #SSEK2: [0, 1, 7200]; scan_type(ASC), SHB_CQCQ_CODE_INDEX(SHB_CQCQ as X), scan_range[var2,var2]  ******* 优化效果点  *******
22              #PRJT2: [0, 72, 144]; exp_num(2), is_atom(FALSE) 
23                #BLKUP2: [0, 72, 144]; IDXFS_DICTIONARY_ITEMS_GROUP_NO(DICTIONARY_ITEMS)
24                  #SSEK2: [0, 72, 144]; scan_type(ASC), IDXFS_DICTIONARY_ITEMS_GROUP_NO(DICTIONARY_ITEMS), scan_range['1008','1008']
25          #BLKUP2: [4, 1, 0]; INDEX33559823(DE)
26            #SSEK2: [4, 1, 0]; scan_type(ASC), INDEX33559823(DEPARTMENT as DE), scan_range[XMXX.DUTY_DEPARTMENT_ID,XMXX.DUTY_DEPARTMENT_ID]
27        #BLKUP2: [4, 7, 0]; SHB_USER_INFO_USERID_INDEX(OWN)
28          #SSEK2: [4, 7, 0]; scan_type(ASC), SHB_USER_INFO_USERID_INDEX(SHB_USER_INFO as OWN), scan_range[XMXX.CREATE_USER_ID,XMXX.CREATE_USER_ID]

2.3 优化结果

这条复杂SQL在客户项目中运行最为频繁,从原来8秒已经优化到200毫秒,极大缩短响应时间,减轻数据库服务器的压力。
优化工作适可而止,不要强迫搞到极致,白白浪费时间,其他项目还得忙。


3. 优化心得

  • 1. 遇到巨长的SQL不要慌张,学会拆分,缩小范围。
  • 2. 学会总结,有时根本不用ET工具,可以直接从执行计划看出来,或者从SQL中某部分能快速怀疑对位置。
  • 3. SQL等价改写的前提是一定要读懂语义,进行同含义的改写(改写成简单的,不是绝对的,有时反而需要改成复杂的才能优化)。
  • 4. 像我们这些DBA一般搞懂语义改写就不错了,能懂业务逻辑那才是最好的,少走很多弯路(遇到很多开发商可能SQL编写能力参差不齐,也有可能不是很熟悉业务,没办法的事)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值