[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.

[20181220]使用提示OR_EXPAND优化.txt的更多相关文章

  1. [20190524]使用use_concat or_expand提示优化.txt

    [20190524]使用use_concat or_expand提示优化.txt --//上午看了链接https://connor-mcdonald.com/2019/05/22/being-gene ...

  2. [20181116]18c DML 日志优化.txt

    [20181116]18c DML 日志优化.txt 1.环境:xxxxxxxx> select banner_full from v$version;BANNER_FULL---------- ...

  3. [20200223]关于latch and mutext的优化.txt

    [20200223]关于latch and mutext的优化.txt --//前一段时间一直在测试使用DBMS_SHARED_POOL.MARKHOT标识热对象以及sql语句的优化.--//有别人问 ...

  4. solr入门之參考淘宝搜索提示功能优化拼音加汉字搜索功能

    首先看一下从淘宝输入搜索keyword获取到的一些数据信息: 第一张:使用拼音的全程来查询 能够看到提示的是匹配的转换的拼音的方式,看最后一个提示项 这里另一个在指定分类文件夹下搜索的功能,难道后台还 ...

  5. dense_rank()+hash提示改写优化SQL

    数据库环境:SQL SERVER 2005 今天看到一条SQL,返回10条数据,执行了50多S.刚好有空,就对它进行了优化,优化后1S出结果. 先看下原始SQL SELECT t1.line_no , ...

  6. nginx-502错误,老是提示busy.优化php-fpm如下

    [global]pid = /usr/local/php/var/run/php-fpm.piderror_log = /usr/local/php/var/log/php-fpm.loglog_le ...

  7. 一百零三:CMS系统之使用sweetalert提示框优化返回结果

    在base模板中引用 在修改密码的js中使用 $(function () { $('#submit').click(function (evnet) { evnet.preventDefault(); ...

  8. sql 优化 链接提示 查询提示 标提示

    SQL Server的查询优化器在select查询执行的时候产生一个高效的查询执行计划.如果优化器不能选择最优的计划,那么就需要检查查询计划.统计信息.支持的索引等,而通过使用提示可以改变优化器选择查 ...

  9. MySQL优化篇(一),我可以和面试官多聊几句吗?——SQL优化流程与优化数据库对象

    我可以和面试官多聊几句吗?只是想偷点技能过来.MySQL优化篇(基于MySQL8.0测试验证),上部分:优化SQL语句.数据库对象,MyISAM表锁和InnoDB锁问题. MyISAM表锁和InnoD ...

随机推荐

  1. 正则表达式的一些探索(偏JavaScript)

    简单的探索下正则表达式的相关知识,首先先了解下正则表达式的引擎和匹配过程区别,再试着掌握如何在场景中编写正则表达式,再然后探索下根据上文已知的原理和编写过程怎么去优化正则表达式,最后给出一些js里正则 ...

  2. Linux文本编辑器vim

    目录 1.vim 的工作模式 2.插入命令 3.定位命令 4.删除命令 5.复制和剪切命令 6.替换和取消命令 7.搜索和搜索替换命令 8.保存和退出命令 9.vim 高级操作 10.总结 通过前面几 ...

  3. Spring Boot (五)Spring Data JPA 操作 MySQL 8

    一.Spring Data JPA 介绍 JPA(Java Persistence API)Java持久化API,是 Java 持久化的标准规范,Hibernate是持久化规范的技术实现,而Sprin ...

  4. 翻译:SET PASSWORD语句(已提交到MariaDB官方手册)

    本文为mariadb官方手册:SET PASSWORD的译文. 原文:https://mariadb.com/kb/en/library/set-password/我提交到MariaDB官方手册的译文 ...

  5. Gitlab仓库搭建及在linux/windows中免密使用gitlab(二)--技术流ken

    Gitlab简介 GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务. 可通过Web界面进行访问公开的或者私人项目.它拥有与Github类似的 ...

  6. SQL Server远程连接 provider: Named Pipes Provider, error: 40 解决方法

    置SQLServer,允许远程连接 按照上面的文章一步步配置后,远程连接出现下面所示的报错(Navicat 和 SQL Server Management Studio) SQL Server Man ...

  7. 月薪15k的测试员需要学习什么技术?

    想了很久,决定还是要写一篇这样的文章出来,月薪15k的测试员需要学习什么技术?我觉得测试想要月薪15k并不难,只要做到我说的这几点肯定是可以的! 克服懒惰 我觉得,越是聪明的人越是觉得自己“懒惰”.大 ...

  8. vbscript 语言通过序列和ADODB实现取号不重复

    目的:通过VBScript脚本利用序列的性质,实现取号不重复 首先,表空间中创建表名为TABLE_YEWID的表格,主要有以下几个字段 -- Create table create table TAB ...

  9. 如何处理Express异常?

    译者按:根据墨菲定律:“有可能出错的事情,就会出错”.那么,既然代码必然会出错,我们就应该处理好异常. 原文: How to handle errors in Express 译者:Fundebug ...

  10. 几点建议帮你写出简洁的JS代码

    译者按: 规范的代码可以有效避免代码bug,fundebug才会报警少一点! 原文: Tips for Writing Cleaner Code 译者: Fundebug 为了保证可读性,本文采用意译 ...