zoukankan      html  css  js  c++  java
  • 帮盖尔优化SQL子查询优化的经典案例

     上周五要下班的时候,盖尔发来一个SQL

    select tpc.policy_id,
           tcm.policy_code,
           tpf.organ_id,
           to_char(tpf.insert_time, 'YYYY-MM-DD') As insert_time,
           tpc.change_id,
           d.policy_code,
           e.company_name,
           f.real_name,
           tpf.fee_type,
           sum(tpf.pay_balance) as pay_balance,
           c.actual_type,
           tpc.notice_code,
           d.policy_type,
           g.mode_name as pay_mode
      from t_policy_change    tpc,
           t_contract_master  tcm,
           t_policy_fee       tpf,
           t_fee_type         c,
           t_contract_master  d,
           t_company_customer e,
           t_customer         f,
           t_pay_mode         g
     where tpc.change_id = tpf.change_id
       and tpf.policy_id = d.policy_id
       and tcm.policy_id = tpc.policy_id
       and tpf.receiv_status = 1 
       and tpf.fee_status = 1
       and tpf.payment_id is null
       and tpf.fee_type = c.type_id
       and tpf.pay_mode = g.mode_id
       and d.company_id = e.company_id(+)
       and d.applicant_id = f.customer_id(+)
       and tpf.organ_id in
           (select 
             organ_id
              from t_company_organ
             start with organ_id = '101'
            connect by prior organ_id = parent_id)
     group by tpc.policy_id,
              tpc.change_id,
              tpf.fee_type,
              to_char(tpf.insert_time, 'YYYY-MM-DD'),
              c.actual_type,
              d.policy_code,
              g.mode_name,
              e.company_name,
              f.real_name,
              tpc.notice_code,
              d.policy_type,
              tpf.organ_id,
              tcm.policy_code
     order by change_id, fee_type
    
    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    | Id  | Operation                           |  Name                       | Rows  | Bytes |TempSpc| Cost (%CPU)|
    ----------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                    |                             | 45962 |    11M|       | 45650   (0)|
    |   1 |  SORT GROUP BY                      |                             | 45962 |    11M|    23M| 45650   (0)|
    |*  2 |   HASH JOIN                         |                             | 45962 |    11M|       | 43908   (0)|
    |   3 |    INDEX FULL SCAN                  | T_FEE_TYPE_IDX_003          |   106 |   636 |       |     1   (0)|
    |   4 |    NESTED LOOPS OUTER               |                             | 45962 |    11M|       | 43906   (0)|
    |*  5 |     HASH JOIN                       |                             | 45962 |  7271K|  6824K| 43905   (0)|
    |   6 |      NESTED LOOPS                   |                             | 45961 |  6283K|       | 42312   (0)|
    |*  7 |       HASH JOIN SEMI                |                             | 45961 |  5655K|    50M| 33120   (1)|
    |*  8 |        HASH JOIN OUTER              |                             |   400K|    45M|    44M| 32315   (1)|
    |*  9 |         HASH JOIN                   |                             |   400K|    39M|    27M| 26943   (0)|
    |* 10 |          HASH JOIN                  |                             |   400K|    23M|       | 16111   (0)|
    |  11 |           TABLE ACCESS FULL         | T_PAY_MODE                  |    25 |   525 |       |     2   (0)|
    |* 12 |           TABLE ACCESS FULL         | T_POLICY_FEE                |   400K|    15M|       | 16107   (0)|
    |  13 |          TABLE ACCESS FULL          | T_CONTRACT_MASTER           |  1136K|    46M|       |  9437   (0)|
    |  14 |         VIEW                        | index_join_007            |  2028K|    30M|       |            |
    |* 15 |          HASH JOIN                  |                             |   400K|    45M|    44M| 32315   (1)|
    |  16 |           INDEX FAST FULL SCAN      | PK_T_CUSTOMER               |  2028K|    30M|       |   548   (0)|
    |  17 |           INDEX FAST FULL SCAN      | IDX_CUSTOMER__BIR_REAL_GEN  |  2028K|    30M|       |   548   (0)|
    |  18 |        VIEW                         | VW_NSO_1                    |     7 |    42 |       |            |
    |* 19 |         CONNECT BY WITH FILTERING   |                             |       |       |       |            |
    |  20 |          NESTED LOOPS               |                             |       |       |       |            |
    |* 21 |           INDEX UNIQUE SCAN         | PK_T_COMPANY_ORGAN          |     1 |     6 |       |            |
    |  22 |           TABLE ACCESS BY USER ROWID| T_COMPANY_ORGAN             |       |       |       |            |
    |  23 |          NESTED LOOPS               |                             |       |       |       |            |
    |  24 |           BUFFER SORT               |                             |     7 |    70 |       |            |
    |  25 |            CONNECT BY PUMP          |                             |       |       |       |            |
    |* 26 |           INDEX RANGE SCAN          | T_COMPANY_ORGAN_IDX_002     |     7 |    70 |       |     1   (0)|
    |  27 |       TABLE ACCESS BY INDEX ROWID   | T_POLICY_CHANGE             |     1 |    14 |       |     2  (50)|
    |* 28 |        INDEX UNIQUE SCAN            | PK_T_POLICY_CHANGE          |     1 |       |       |     1   (0)|
    |  29 |      INDEX FAST FULL SCAN           | IDX1_ACCEPT_DATE            |  1136K|    23M|       |   899   (0)|
    |  30 |     TABLE ACCESS BY INDEX ROWID     | T_COMPANY_CUSTOMER          |     1 |    90 |       |     2  (50)|
    |* 31 |      INDEX UNIQUE SCAN              | PK_T_COMPANY_CUSTOMER       |     1 |       |       |            |
    ----------------------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - access("TPF"."FEE_TYPE"="C"."TYPE_ID")
       5 - access("TCM"."POLICY_ID"="TPC"."POLICY_ID")
       7 - access("TPF"."ORGAN_ID"="VW_NSO_1"."$nso_col_1")
       8 - access("D"."APPLICANT_ID"="F"."CUSTOMER_ID"(+))
       9 - access("TPF"."POLICY_ID"="D"."POLICY_ID")
      10 - access("TPF"."PAY_MODE"="G"."MODE_ID")
      12 - filter("TPF"."CHANGE_ID" IS NOT NULL AND TO_NUMBER("TPF"."RECEIV_STATUS")=1 AND "TPF"."FEE_STATUS"=1 AND
                  "TPF"."PAYMENT_ID" IS NULL)
      15 - access("indexjoin_alias_012".ROWID="indexjoin_alias_011".ROWID)
      19 - filter("T_COMPANY_ORGAN"."ORGAN_ID"='101')
      21 - access("T_COMPANY_ORGAN"."ORGAN_ID"='101')
      26 - access("T_COMPANY_ORGAN"."PARENT_ID"=NULL)
      28 - access("TPC"."CHANGE_ID"="TPF"."CHANGE_ID")
      31 - access("D"."COMPANY_ID"="E"."COMPANY_ID"(+))
    
    55 rows selected
    
    Statistics
    ----------------------------------------------------------
             21  recursive calls
              0  db block gets
         125082  consistent gets
          21149  physical reads
              0  redo size
           2448  bytes sent via SQL*Net to client
            656  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
              4  sorts (memory)
              0  sorts (disk)
             11  rows processed


     


    这个SQL要21秒才能跑完,逻辑读12W左右,问我能不能优化。优化这个SQL我只花了1分钟左右的时间,因为太简单了
    你们看这个SQL是典型的JOIN,对付这种SQL肯定要让表走索引,但是从执行计划上看有个1千万行的表T_CONTRACT_MASTER走的是全表扫描,
    T_POLICY_FEE 这个400W行的表也是走全表扫描,那么它不慢才怪呢,然后SQL的过滤条件有个 in 子查询
           (select
             organ_id
              from t_company_organ
             start with organ_id = '101'
            connect by prior organ_id = parent_id)
    从执行计划上看,CBO对这儿子查询进行了unnest,因为通常情况下CBO认为子查询被unnest之后性能好于filter       

    于是我让盖尔查询 子查询返回多少行        
    select  organ_id
              from t_company_organ
             start with organ_id = '101'
            connect by prior organ_id = parent_id   ---盖尔说它返回1行        

    对于子查询,如果它返回数据很少(这里返回1行),那么可以让它走filter, 而且filter基本上是在SQL最后去阶段执行,这样t_policy_fee就可以走索引了
    所以我给这个子查询加了个HINT,禁止子查询扩展

    select tpc.policy_id,
           tcm.policy_code,
           tpf.organ_id,
           to_char(tpf.insert_time, 'YYYY-MM-DD') As insert_time,
           tpc.change_id,
           d.policy_code,
           e.company_name,
           f.real_name,
           tpf.fee_type,
           sum(tpf.pay_balance) as pay_balance,
           c.actual_type,
           tpc.notice_code,
           d.policy_type,
           g.mode_name as pay_mode
      from t_policy_change    tpc,
           t_contract_master  tcm,
           t_policy_fee       tpf,
           t_fee_type         c,
           t_contract_master  d,
           t_company_customer e,
           t_customer         f,
           t_pay_mode         g
     where tpc.change_id = tpf.change_id
       and tpf.policy_id = d.policy_id
       and tcm.policy_id = tpc.policy_id
       and tpf.receiv_status = '1'  ---这里原来没引号,是开发那SB搞忘了写'',我让盖尔添加上了,不添加上就没法用索引
       and tpf.fee_status = 1
       and tpf.payment_id is null
       and tpf.fee_type = c.type_id
       and tpf.pay_mode = g.mode_id
       and d.company_id = e.company_id(+)
       and d.applicant_id = f.customer_id(+)
       and tpf.organ_id in
           (select /*+ no_unnest */     --此处的HINT后加的
             organ_id
              from t_company_organ
             start with organ_id = '101'
            connect by prior organ_id = parent_id)
     group by tpc.policy_id,
              tpc.change_id,
              tpf.fee_type,
              to_char(tpf.insert_time, 'YYYY-MM-DD'),
              c.actual_type,
              d.policy_code,
              g.mode_name,
              e.company_name,
              f.real_name,
              tpc.notice_code,
              d.policy_type,
              tpf.organ_id,
              tcm.policy_code
     order by change_id, fee_type
    
    SQL> select * from table(dbms_xplan.display);
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------------------------------------------
    | Id  | Operation                            |  Name                          | Rows  | Bytes |TempSpc| Cost (%CPU)|
    --------------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                     |                                | 20026 |  4928K|       | 68615  (30)|
    |   1 |  SORT GROUP BY                       |                                | 20026 |  4928K|    10M| 28563   (0)|
    |*  2 |   FILTER                             |                                |       |       |       |            |
    |   3 |    NESTED LOOPS                      |                                | 20026 |  4928K|       | 27812   (0)|
    |   4 |     NESTED LOOPS                     |                                | 20026 |  4498K|       | 23807   (0)|
    |   5 |      NESTED LOOPS OUTER              |                                | 20026 |  4224K|       | 19802   (0)|
    |   6 |       NESTED LOOPS OUTER             |                                | 20026 |  3911K|       | 15797   (0)|
    |   7 |        NESTED LOOPS                  |                                | 20026 |  2151K|       | 15796   (0)|
    |*  8 |         HASH JOIN                    |                                | 20026 |  1310K|       | 11791   (0)|
    |   9 |          INDEX FULL SCAN             | T_FEE_TYPE_IDX_003             |   106 |   636 |       |     1   (0)|
    |* 10 |          HASH JOIN                   |                                | 20026 |  1192K|       | 11789   (0)|
    |  11 |           TABLE ACCESS FULL          | T_PAY_MODE                     |    25 |   525 |       |     2   (0)|
    |* 12 |           TABLE ACCESS BY INDEX ROWID| T_POLICY_FEE                   | 20026 |   782K|       | 11786   (0)|
    |* 13 |            INDEX RANGE SCAN          | IDX_POLICY_FEE__RECEIV_STATUS  |  1243K|       |       | 10188   (0)|
    |  14 |         TABLE ACCESS BY INDEX ROWID  | T_CONTRACT_MASTER              |     1 |    43 |       |     2  (50)|
    |* 15 |          INDEX UNIQUE SCAN           | PK_T_CONTRACT_MASTER           |     1 |       |       |     1   (0)|
    |  16 |        TABLE ACCESS BY INDEX ROWID   | T_COMPANY_CUSTOMER             |     1 |    90 |       |     2  (50)|
    |* 17 |         INDEX UNIQUE SCAN            | PK_T_COMPANY_CUSTOMER          |     1 |       |       |            |
    |  18 |       TABLE ACCESS BY INDEX ROWID    | T_CUSTOMER                     |     1 |    16 |       |     2  (50)|
    |* 19 |        INDEX UNIQUE SCAN             | PK_T_CUSTOMER                  |     1 |       |       |     1   (0)|
    |  20 |      TABLE ACCESS BY INDEX ROWID     | T_POLICY_CHANGE                |     1 |    14 |       |     2  (50)|
    |* 21 |       INDEX UNIQUE SCAN              | PK_T_POLICY_CHANGE             |     1 |       |       |     1   (0)|
    |  22 |     TABLE ACCESS BY INDEX ROWID      | T_CONTRACT_MASTER              |     1 |    22 |       |     2  (50)|
    |* 23 |      INDEX UNIQUE SCAN               | PK_T_CONTRACT_MASTER           |     1 |       |       |     1   (0)|
    |* 24 |    FILTER                            |                                |       |       |       |            |
    |* 25 |     CONNECT BY WITH FILTERING        |                                |       |       |       |            |
    |  26 |      NESTED LOOPS                    |                                |       |       |       |            |
    |* 27 |       INDEX UNIQUE SCAN              | PK_T_COMPANY_ORGAN             |     1 |     6 |       |            |
    |  28 |       TABLE ACCESS BY USER ROWID     | T_COMPANY_ORGAN                |       |       |       |            |
    |  29 |      NESTED LOOPS                    |                                |       |       |       |            |
    |  30 |       BUFFER SORT                    |                                |     7 |    70 |       |            |
    |  31 |        CONNECT BY PUMP               |                                |       |       |       |            |
    |* 32 |       INDEX RANGE SCAN               | T_COMPANY_ORGAN_IDX_002        |     7 |    70 |       |     1   (0)|
    --------------------------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "T_COMPANY_ORGAN" "T_COMPANY_ORGAN" WHERE
                  "T_COMPANY_ORGAN"."PARENT_ID"=NULL AND ("T_COMPANY_ORGAN"."ORGAN_ID"=:B1)))
       8 - access("SYS_ALIAS_1"."FEE_TYPE"="C"."TYPE_ID")
      10 - access("SYS_ALIAS_1"."PAY_MODE"="G"."MODE_ID")
      12 - filter("SYS_ALIAS_1"."CHANGE_ID" IS NOT NULL AND "SYS_ALIAS_1"."FEE_STATUS"=1 AND "SYS_ALIAS_1"."PAYMENT_ID"
                  IS NULL)
      13 - access("SYS_ALIAS_1"."RECEIV_STATUS"='1')
      15 - access("SYS_ALIAS_1"."POLICY_ID"="D"."POLICY_ID")
      17 - access("D"."COMPANY_ID"="E"."COMPANY_ID"(+))
      19 - access("D"."APPLICANT_ID"="F"."CUSTOMER_ID"(+))
      21 - access("TPC"."CHANGE_ID"="SYS_ALIAS_1"."CHANGE_ID")
      23 - access("TCM"."POLICY_ID"="TPC"."POLICY_ID")
      24 - filter("T_COMPANY_ORGAN"."ORGAN_ID"=:B1)
      25 - filter("T_COMPANY_ORGAN"."ORGAN_ID"='101')
      27 - access("T_COMPANY_ORGAN"."ORGAN_ID"='101')
      32 - access("T_COMPANY_ORGAN"."PARENT_ID"=NULL)
    
    58 rows selected.
    
    Statistics
    ----------------------------------------------------------
              0  recursive calls
              0  db block gets
           2817  consistent gets
              0  physical reads
              0  redo size
           2268  bytes sent via SQL*Net to client
            656  bytes received via SQL*Net from client
              2  SQL*Net roundtrips to/from client
             40  sorts (memory)
              0  sorts (disk)
              9  rows processed
    


    最终这个SQL能在1秒以内跑完,逻辑读下降到2817 ,到此我就没继续优化了,这个时候停止优化吧,别的了强迫优化症
    这个优化案例很简单,我都不好意思贴在博客上,通过这个文章你要学到的就是,如果子查询返回数据很少,那么不妨让它走filter        

  • 相关阅读:
    python读取文件的方法
    python中global 和 nonlocal 的作用域
    android环境安装及配置
    python学习——sys.argv
    python学习——urlparse模块
    android:cmd下面用adb打log
    获取系统的换行符
    python----字符串方法
    类的继承---多重继承(两个父类有相同方法名和参数)
    Djngo 请求的生命周期
  • 原文地址:https://www.cnblogs.com/hehe520/p/6330557.html
Copyright © 2011-2022 走看看