• [20181220]使用提示OR_EXPAND优化.txt


    [20181220]使用提示OR_EXPAND优化.txt

    --//链接http://www.itpub.net/thread-2107240-2-1.html,http://www.itpub.net/thread-2107231-2-1.html的讨论.
    --//ZALBB建议在18c下尝试看看,我们这里仅仅1台18c,而且还是生产系统,正好前几天在办公机器重新安装12c,在12c测试看看.
    --//主要问题感觉oracle对于这样的sql有点奇怪....

    1.环境:
    SCOTT@test01p> @ ver1
    PORT_STRING                    VERSION        BANNER                                                                               CON_ID
    ------------------------------ -------------- -------------------------------------------------------------------------------- ----------
    IBMPC/WIN_NT64-9.1.0           12.2.0.1.0     Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production              0

    create table t1 as select rownum id1 ,rownum id2 ,lpad('x',100,'x') name from dual connect by level<=6000;
    create table t2 as select rownum id1 ,rownum id2 ,lpad('x',100,'x') name from dual connect by level<=6000;
    create index i_t1_id1 on t1(id1);
    create index i_t1_id2 on t1(id2);
    create index i_t2_id1 on t2(id1);

    --//分析略.

    2.测试:
    SCOTT@test01p> alter session set statistics_level = all;
    Session altered.

    SCOTT@test01p> select  * from t1 where t1.id1 in  (select  t2.id1 from t2 where t2.id1=11 ) or  (t1.id2=10 );
           ID1        ID2 NAME
    ---------- ---------- ----------------------------------------------------------------------------------------------------
            10         10 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
            11         11 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    SCOTT@test01p> @ dpc '' ''
    PLAN_TABLE_OUTPUT
    -------------------------------------
    SQL_ID  gz5pqkg6svm7k, child number 0
    -------------------------------------
    select  * from t1 where t1.id1 in  (select  t2.id1 from t2 where
    t2.id1=11 ) or  (t1.id2=10 )
    Plan hash value: 1962644737
    -------------------------------------------------------------------------------------------------------------------------
    | Id  | Operation          | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
    -------------------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT   |          |      1 |        |       |    30 (100)|          |      2 |00:00:00.01 |     115 |
    |*  1 |  FILTER            |          |      1 |        |       |            |          |      2 |00:00:00.01 |     115 |
    |   2 |   TABLE ACCESS FULL| T1       |      1 |   6000 |   638K|    30   (0)| 00:00:01 |   6000 |00:00:00.01 |     113 |
    |*  3 |   FILTER           |          |   5999 |        |       |            |          |      1 |00:00:00.01 |       2 |
    |*  4 |    INDEX RANGE SCAN| I_T2_ID1 |      1 |      1 |     4 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       2 |
    -------------------------------------------------------------------------------------------------------------------------
    Query Block Name / Object Alias (identified by operation id):
    -------------------------------------------------------------
       1 - SEL$1
       2 - SEL$1 / T1@SEL$1
       3 - SEL$2
       4 - SEL$2 / T2@SEL$2
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       1 - filter(("T1"."ID2"=10 OR  IS NOT NULL))
       3 - filter(11=:B1)
       4 - access("T2"."ID1"=:B1)
    32 rows selected.

    --//执行计划存在1个全表扫描.里面的索引选择性很好,oracle并没有选择合理的执行计划.
    --//而且有1个小小的细节,id=4的starts=1,而前面的id=3的starts=5999.你可以看出这里oracle显示执行计划有1个小小的bug.
    --//id=4的starts应该是5999.这样看到的逻辑读不应该是后面的2而是2*5999 = 11998.
    --//而且你可以看出oracle忽略的id=4多次INDEX RANGE SCAN的成本.
    --//链接http://www.itpub.net/thread-2107240-2-1.html里面的显示倒是正确的.它的版本是11.2.0.4.180717.

    3.是否通过提示优化sql语句:
    --//首先想到的是USE_CONCAT.
    select /*+ USE_CONCAT(@"SEL$1" 8 OR_PREDICATES(1)) */ * from t1 where t1.id1 in  (select /*+unnest */ t2.id1 from t2 where t2.id1=11 ) or  (t1.id2=10 );

    --//执行计划如下:
    -------------------------------------------------------------------------------------------------------------------------------------------
    | Id  | Operation                            | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
    -------------------------------------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                     |          |      1 |        |       |    33 (100)|          |      2 |00:00:00.01 |     118 |
    |   1 |  CONCATENATION                       |          |      1 |        |       |            |          |      2 |00:00:00.01 |     118 |
    |   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T1       |      1 |      1 |   109 |     2   (0)| 00:00:01 |      1 |00:00:00.01 |       4 |
    |*  3 |    INDEX RANGE SCAN                  | I_T1_ID2 |      1 |      1 |       |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
    |*  4 |   FILTER                             |          |      1 |        |       |            |          |      1 |00:00:00.01 |     114 |
    |*  5 |    TABLE ACCESS FULL                 | T1       |      1 |   5999 |   638K|    30   (0)| 00:00:01 |   5999 |00:00:00.01 |     112 |
    |*  6 |    FILTER                            |          |   5999 |        |       |            |          |      1 |00:00:00.01 |       2 |
    |*  7 |     INDEX RANGE SCAN                 | I_T2_ID1 |      1 |      1 |     4 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       2 |
    -------------------------------------------------------------------------------------------------------------------------------------------
    Query Block Name / Object Alias (identified by operation id):
    -------------------------------------------------------------
       1 - SEL$1
       2 - SEL$1_1 / T1@SEL$1
       3 - SEL$1_1 / T1@SEL$1
       5 - SEL$1_2 / T1@SEL$1_2
       6 - SEL$2
       7 - SEL$2   / T2@SEL$2
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       3 - access("T1"."ID2"=10)
       4 - filter( IS NOT NULL)
       5 - filter(LNNVL("T1"."ID2"=10))
       6 - filter(11=:B1)
       7 - access("T2"."ID1"=:B1)

    --//很奇怪id=4,依旧选择过滤,unnest提示没有用.实际上使用USE_CONCAT相当每个or分支加入LNNVL(条件)来排他符合条件的记录.
    --//也就是oracle依旧选择的执行计划不是很理想,甚至比前面还要差.

    4.尝试OR_EXPAND提示:
    select /*+ OR_EXPAND */ * from t1 where t1.id1 in  (select  /*+ unnest */ t2.id1 from t2 where t2.id1=11 ) or  (t1.id2=10 );

    --//执行计划如下:
    Plan hash value: 1716482303
    ----------------------------------------------------------------------------------------------------------------------------------------------------
    | Id  | Operation                              | Name            | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
    ----------------------------------------------------------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                       |                 |      1 |        |       |     5 (100)|          |      2 |00:00:00.01 |       9 |
    |   1 |  VIEW                                  | VW_ORE_BA8ECEFB |      1 |      2 |   156 |     5   (0)| 00:00:01 |      2 |00:00:00.01 |       9 |
    |   2 |   UNION-ALL                            |                 |      1 |        |       |            |          |      2 |00:00:00.01 |       9 |
    |   3 |    TABLE ACCESS BY INDEX ROWID BATCHED | T1              |      1 |      1 |   109 |     2   (0)| 00:00:01 |      1 |00:00:00.01 |       4 |
    |*  4 |     INDEX RANGE SCAN                   | I_T1_ID2        |      1 |      1 |       |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
    |   5 |    NESTED LOOPS SEMI                   |                 |      1 |      1 |   113 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       5 |
    |*  6 |     TABLE ACCESS BY INDEX ROWID BATCHED| T1              |      1 |      1 |   109 |     2   (0)| 00:00:01 |      1 |00:00:00.01 |       3 |
    |*  7 |      INDEX RANGE SCAN                  | I_T1_ID1        |      1 |      1 |       |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       2 |
    |*  8 |     INDEX RANGE SCAN                   | I_T2_ID1        |      1 |      1 |     4 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       2 |
    ----------------------------------------------------------------------------------------------------------------------------------------------------
    Query Block Name / Object Alias (identified by operation id):
    -------------------------------------------------------------
       1 - SET$9162BF3C   / VW_ORE_BA8ECEFB@SEL$BA8ECEFB
       2 - SET$9162BF3C
       3 - SET$9162BF3C_1 / T1@SEL$1
       4 - SET$9162BF3C_1 / T1@SEL$1
       5 - SEL$C90BA1D5
       6 - SEL$C90BA1D5   / T1@SEL$1
       7 - SEL$C90BA1D5   / T1@SEL$1
       8 - SEL$C90BA1D5   / T2@SEL$2
    Outline Data
    -------------
      /*+
          BEGIN_OUTLINE_DATA
          IGNORE_OPTIM_EMBEDDED_HINTS
          OPTIMIZER_FEATURES_ENABLE('12.2.0.1')
          DB_VERSION('12.2.0.1')
          ALL_ROWS
          OUTLINE_LEAF(@"SEL$C90BA1D5")
          UNNEST(@"SEL$2")
          OUTLINE_LEAF(@"SET$9162BF3C_1")
          OUTLINE_LEAF(@"SET$9162BF3C")
          OR_EXPAND(@"SEL$1" (1) (2))
          OUTLINE_LEAF(@"SEL$BA8ECEFB")
          OUTLINE(@"SET$9162BF3C_2")
          OUTLINE(@"SEL$2")
          OUTLINE(@"SET$9162BF3C")
          OR_EXPAND(@"SEL$1" (1) (2))
          OUTLINE(@"SEL$1")
          NO_ACCESS(@"SEL$BA8ECEFB" "VW_ORE_BA8ECEFB"@"SEL$BA8ECEFB")
          INDEX_RS_ASC(@"SET$9162BF3C_1" "T1"@"SEL$1" ("T1"."ID2"))
          BATCH_TABLE_ACCESS_BY_ROWID(@"SET$9162BF3C_1" "T1"@"SEL$1")
          INDEX_RS_ASC(@"SEL$C90BA1D5" "T1"@"SEL$1" ("T1"."ID1"))
          BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$C90BA1D5" "T1"@"SEL$1")
          INDEX(@"SEL$C90BA1D5" "T2"@"SEL$2" ("T2"."ID1"))
          LEADING(@"SEL$C90BA1D5" "T1"@"SEL$1" "T2"@"SEL$2")
          USE_NL(@"SEL$C90BA1D5" "T2"@"SEL$2")
          END_OUTLINE_DATA
      */
    Predicate Information (identified by operation id):
    ---------------------------------------------------
       4 - access("T1"."ID2"=10)
       6 - filter(LNNVL("T1"."ID2"=10))
       7 - access("T1"."ID1"=11)
       8 - access("T2"."ID1"=11)
           filter("T1"."ID1"="T2"."ID1")

    --//12c下oracle选择正确的执行计划.可以发现id=2使用UNION-ALL,也就是oracle做了查询转换成union all的形式.
    --//另外我曾经尝试将ounline date的提示信息加入到11g环境,执行计划依旧没有选择OR_EXPAND.
    --//通过10053事件看看.

    SCOTT@test01p> @ 10053x cg5kmfhgczjfd 0
    PL/SQL procedure successfully completed.

    ORE: after OR Expansion:******* UNPARSED QUERY IS *******
    SELECT "VW_ORE_BA8ECEFB"."ITEM_1" "ID1","VW_ORE_BA8ECEFB"."ITEM_2" "ID2","VW_ORE_BA8ECEFB"."ITEM_3" "NAME" FROM  ( (SELECT "T1"."ID1" "ITEM_1","T1"."ID2" "ITEM_2","T1"."NAME" "ITEM_3" FROM "SCOTT"."T1" "T1" WHERE "T1"."ID2"=10) UNION ALL  (SELECT "T1"."ID1" "ITEM_1","T1"."ID2" "ITEM_2","T1"."NAME" "ITEM_3" FROM "SCOTT"."T1" "T1" WHERE "T1"."ID1"=ANY (SELECT /*+ UNNEST */ "T2"."ID1" "ID1" FROM "SCOTT"."T2" "T2" WHERE "T2"."ID1"=11) AND LNNVL("T1"."ID2"=10))) "VW_ORE_BA8ECEFB"

    --//格式化显示如下:
    SELECT "VW_ORE_BA8ECEFB"."ITEM_1" "ID1"
          ,"VW_ORE_BA8ECEFB"."ITEM_2" "ID2"
          ,"VW_ORE_BA8ECEFB"."ITEM_3" "NAME"
      FROM ( (SELECT "T1"."ID1" "ITEM_1"
                    ,"T1"."ID2" "ITEM_2"
                    ,"T1"."NAME" "ITEM_3"
                FROM "SCOTT"."T1" "T1"
               WHERE "T1"."ID2" = 10)
            UNION ALL
            (SELECT "T1"."ID1" "ITEM_1"
                   ,"T1"."ID2" "ITEM_2"
                   ,"T1"."NAME" "ITEM_3"
               FROM "SCOTT"."T1" "T1"
              WHERE     "T1"."ID1" = ANY (SELECT /*+ UNNEST */
                                                "T2"."ID1" "ID1"
                                            FROM "SCOTT"."T2" "T2"
                                           WHERE "T2"."ID1" = 11)
                    AND LNNVL ("T1"."ID2" = 10))) "VW_ORE_BA8ECEFB";

    --//也就是oracle查询转换为 UNION ALL的形式.
    --//你可以看到第2个条件人为的加入LNNVL ("T1"."ID2" = 10).
    --// OR_EXPAND 提示 与 USE_CONCAT 提示到底有什么不同?

    5.补充使用USE_CONCAT看到的情况:

    select /*+ USE_CONCAT(@"SEL$1" 8 OR_PREDICATES(1)) */ * from t1 where t1.id1 in  (select /*+unnest */ t2.id1 from t2 where t2.id1=11 ) or  (t1.id2=10 );

    SCOTT@test01p> @ 10053x 18h6hkqcqq3w2 0
    PL/SQL procedure successfully completed.

    --//看这些太烦,不过可以发现如下:
    LORE: Or-Expansion validity checks failed on query block SEL$2 (#2) because Cost based OR expansion enabled

    SYS@test01p> @ hide or_exp
    old  10:  and lower(a.ksppinm) like lower('%&1%')
    new  10:  and lower(a.ksppinm) like lower('%or_exp%')
    NAME                               DESCRIPTION                                       DEFAULT_VALUE SESSION_VALUE SYSTEM_VALUE
    ---------------------------------- ------------------------------------------------- ------------- ------------- ------------
    _no_or_expansion                   OR expansion during optimization disabled         TRUE          FALSE         FALSE
    _optimizer_cbqt_or_expansion       enables cost based OR expansion                   TRUE          ON            ON
    _optimizer_interleave_or_expansion interleave OR Expansion during CBQT               TRUE          TRUE          TRUE
    _optimizer_or_expansion            control or expansion approach used                TRUE          DEPTH         DEPTH
    _optimizer_or_expansion_subheap    Use subheap for optimizer or-expansion            TRUE          TRUE          TRUE
    _or_expand_nvl_predicate           enable OR expanded plan for NVL/DECODE predicate  TRUE          TRUE          TRUE
    6 rows selected.
    --//也就是12c缺省打开因为以上原因.不过我尝试"_optimizer_cbqt_or_expansion"=off也无效.放弃!!

    --//我也尝试提高全表扫描的成本看看是否执行计划会发生改变,不过依旧没用.
    SCOTT@test01p> exec dbms_stats.SET_TABLE_STATS(user,'T1',NUMBLKS=>800000000000);
    PL/SQL procedure successfully completed.

  • 相关阅读:
    无线网络安全一瞥
    静态代码分析与代码质量安全
    构建移动应用测试(一)
    移动应用App测试与质量管理一
    企业商务差旅信息化管理与移动App
    身份管理系统与解决方案
    微信指数是怎么调取数据来源的
    微信正式上线“微信指数”,基于微信大数据分析的移动端指数
    新版微信小程序即将上线 新增微信支付功能
    微信公众号留言置顶功能上线
  • 原文地址:https://www.cnblogs.com/lfree/p/10150079.html
Copyright © 2020-2023  润新知