[20160111]使用提示no_unnest.txt

简介: [20160111]使用提示no_unnest.txt --今天被人问及这个提示,我记忆使用这个提示,就会出现filter过滤条件.我以前遇到这种语句,一般两者提示都使用看看那个效果好.

[20160111]使用提示no_unnest.txt

--今天被人问及这个提示,我记忆使用这个提示,就会出现filter过滤条件.我以前遇到这种语句,一般两者提示都使用看看那个效果好.
--英文的理解有时候很烦,nest表示嵌套,no_unnest 2次取反也表示嵌套.
--还是通过例子来说明问题:

1.环境:
SCOTT@book> @ &r/ver1
PORT_STRING                    VERSION        BANNER
------------------------------ -------------- --------------------------------------------------------------------------------
x86_64/Linux 2.4.xx            11.2.0.4.0     Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production

SCOTT@book> create table t11 as select rownum id,lpad(rownum,10,'t11') name from dual connect by level<=100;
Table created.

SCOTT@book> create table t12 as select rownum id,lpad(rownum,100,'t12') name from dual connect by level<=1e5;
Table created.

--分析表使用Method_Opt => 'FOR ALL COLUMNS SIZE 1 '.

2.测试:
select * from t11 where exists (select null from t12 where t12.id=t11.id);
select * from t11 where id in (select id from t12 );

--以上两者写法执行计划都是一样,实际上现在许多情况不需考虑到底还是使用in 还是exists.

Plan hash value: 3627696898
------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       |   432 (100)|          |    100 |00:00:00.01 |       7 |       |       |          |
|*  1 |  HASH JOIN SEMI    |      |      1 |    100 |  1900 |   432   (1)| 00:00:06 |    100 |00:00:00.01 |       7 |  1519K|  1519K| 1546K (0)|
|   2 |   TABLE ACCESS FULL| T11  |      1 |    100 |  1400 |     3   (0)| 00:00:01 |    100 |00:00:00.01 |       2 |       |       |          |
|   3 |   TABLE ACCESS FULL| T12  |      1 |    100K|   488K|   429   (1)| 00:00:06 |    100 |00:00:00.01 |       5 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T11@SEL$1
   3 - SEL$5DA710D3 / T12@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("T12"."ID"="T11"."ID")

--如果你仔细看执行计划可以发现一个细节,T12执行的是全表扫描,但是逻辑读仅仅5个,很明显全表扫描T12的逻辑读不可能能这么小,这
--个是因为前面的100条已经满足全部的输出,从A-Rows=100也可以看出问题在那里.

3.如果我修改T12表,ID=100变成id=1e5+100呢?会出现什么情况呢?

SCOTT@book> update t12 set id=1e5+100 where id=100;
1 row updated.

SCOTT@book> commit ;
Commit complete.

Plan hash value: 3627696898
------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       |   432 (100)|          |     99 |00:00:00.06 |    1546 |       |       |          |
|*  1 |  HASH JOIN SEMI    |      |      1 |    100 |  1900 |   432   (1)| 00:00:06 |     99 |00:00:00.06 |    1546 |  1519K|  1519K| 1528K (0)|
|   2 |   TABLE ACCESS FULL| T11  |      1 |    100 |  1400 |     3   (0)| 00:00:01 |    100 |00:00:00.01 |       2 |       |       |          |
|   3 |   TABLE ACCESS FULL| T12  |      1 |    100K|   488K|   429   (1)| 00:00:06 |    100K|00:00:00.02 |    1544 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T11@SEL$1
   3 - SEL$5DA710D3 / T12@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("T12"."ID"="T11"."ID")

--因为这个时候T12表ID=100的记录不存在,在做HASH JOIN SEMI连接的时候必须扫描整个T12表,才知道结果.

--修改回来:
SCOTT@book> update t12 set id=100 where id=1e5+100;
1 row updated.

SCOTT@book> commit ;
Commit complete.

4.使用提示no_unnest看看:

select * from t11 where exists (select /*+ no_unnest */ null from t12 where t12.id=t11.id);
select * from t11 where id in (select /*+ no_unnest */ id from t12 );

Plan hash value: 2069773301
---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       | 21445 (100)|          |    100 |00:00:00.01 |     337 |
|*  1 |  FILTER            |      |      1 |        |       |            |          |    100 |00:00:00.01 |     337 |
|   2 |   TABLE ACCESS FULL| T11  |      1 |    100 |  1400 |     3   (0)| 00:00:01 |    100 |00:00:00.01 |       3 |
|*  3 |   TABLE ACCESS FULL| T12  |    100 |      1 |     5 |   429   (1)| 00:00:06 |    100 |00:00:00.01 |     334 |
---------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T11@SEL$1
   3 - SEL$2 / T12@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NOT NULL)
   3 - filter("ID"=:B1)

--逻辑读上升到337. 以前我总以为使用在这种情况下ID=1使用filter效果不好.实际上许多情况下要具体问题具体分析.
--以上执行计划从T11取一行,然后扫描T12时只要遇到满足执行计划的情况停止扫描,输出该行.相当于扫描100次,因为我满足记录的行靠
--前,逻辑读不是很高.

5.如果我修改T12表,ID=100变成id=1e5+100呢?会出现什么情况呢?
SCOTT@book> update t12 set id=1e5+100 where id=100;
1 row updated.

SCOTT@book> commit ;
Commit complete.

select * from t11 where exists (select /*+ no_unnest */ null from t12 where t12.id=t11.id);
select * from t11 where id in (select /*+ no_unnest */ id from t12 );

Plan hash value: 2069773301

---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       | 21445 (100)|          |     99 |00:00:00.01 |    1876 |
|*  1 |  FILTER            |      |      1 |        |       |            |          |     99 |00:00:00.01 |    1876 |
|   2 |   TABLE ACCESS FULL| T11  |      1 |    100 |  1400 |     3   (0)| 00:00:01 |    100 |00:00:00.01 |       3 |
|*  3 |   TABLE ACCESS FULL| T12  |    100 |      1 |     5 |   429   (1)| 00:00:06 |     99 |00:00:00.01 |    1873 |
---------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T11@SEL$1
   3 - SEL$2 / T12@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NOT NULL)
   3 - filter("T12"."ID"=:B1)

--因为这个时候T12表ID=100的记录不存在,在T11的id=100时做扫描T12时必须扫描整个T12表,才知道结果.这样导致最后一次逻辑读很大.
--当然我没有建立索引,如果建立索引就没有这种情况.

6.这样什么时候使用no_unnest获得好的效果呢?

--修改回来:
SCOTT@book> update t12 set id=100 where id=1e5+100;
1 row updated.

SCOTT@book> commit ;
Commit complete.

SCOTT@book> update t11 set id=1e5;
100 rows updated.

SCOTT@book> commit ;
Commit complete.

--把T11全部修改一个值1e5.

select * from t11 where exists (select /*+ no_unnest */ null from t12 where t12.id=t11.id);
select * from t11 where id in (select /*+ no_unnest */ id from t12 );

Plan hash value: 2069773301
---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       | 21445 (100)|          |    100 |00:00:00.01 |    1546 |
|*  1 |  FILTER            |      |      1 |        |       |            |          |    100 |00:00:00.01 |    1546 |
|   2 |   TABLE ACCESS FULL| T11  |      1 |    100 |  1400 |     3   (0)| 00:00:01 |    100 |00:00:00.01 |       3 |
|*  3 |   TABLE ACCESS FULL| T12  |      1 |      1 |     5 |   429   (1)| 00:00:06 |      1 |00:00:00.01 |    1543 |
---------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T11@SEL$1
   3 - SEL$2 / T12@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NOT NULL)
   3 - filter("T12"."ID"=:B1)

--因为T11里面id都是一样的等于1e5,可以发现仅仅扫描T12一次(Starts=1).
--如果T11的id=1这样逻辑读更小,继续测试看看.

SCOTT@book> update t11 set id=1;
100 rows updated.

SCOTT@book> commit ;
Commit complete.

Plan hash value: 2069773301
---------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       | 21445 (100)|          |    100 |00:00:00.01 |       6 |
|*  1 |  FILTER            |      |      1 |        |       |            |          |    100 |00:00:00.01 |       6 |
|   2 |   TABLE ACCESS FULL| T11  |      1 |    100 |  1400 |     3   (0)| 00:00:01 |    100 |00:00:00.01 |       3 |
|*  3 |   TABLE ACCESS FULL| T12  |      1 |      1 |     5 |   429   (1)| 00:00:06 |      1 |00:00:00.01 |       3 |
---------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1
   2 - SEL$1 / T11@SEL$1
   3 - SEL$2 / T12@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NOT NULL)
   3 - filter("T12"."ID"=:B1)

--可以发现在这样的情况逻辑读仅仅6个.

总结:
--希望以上的测试,能够帮助大家理解这种提示的作用.

--补充最后不用提示的情况:
Plan hash value: 3627696898
------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |       |   432 (100)|          |    100 |00:00:00.01 |       5 |       |       |          |
|*  1 |  HASH JOIN SEMI    |      |      1 |    100 |  1900 |   432   (1)| 00:00:06 |    100 |00:00:00.01 |       5 |  1519K|  1519K|  684K (0)|
|   2 |   TABLE ACCESS FULL| T11  |      1 |    100 |  1400 |     3   (0)| 00:00:01 |    100 |00:00:00.01 |       2 |       |       |          |
|   3 |   TABLE ACCESS FULL| T12  |      1 |    100K|   488K|   429   (1)| 00:00:06 |      1 |00:00:00.01 |       3 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T11@SEL$1
   3 - SEL$5DA710D3 / T12@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("T12"."ID"="T11"."ID")

--因为T11的id值都一样,仅仅扫描T12一次,并且因为很快定位.逻辑读很小.

目录
相关文章
|
10月前
|
弹性计算 运维 Shell
批量将txt 文件修改为doc 文件
【4月更文挑战第29天】
76 1
|
Shell 关系型数据库 Oracle
[20180302]使用find命令小错误.txt
[20180302]使用find命令小错误.txt --//上午一台机器磁盘空间爆满,腾出磁盘空间顺便清理adump目录文件,再次遇到小问题.做一个记录. --//我使用find遇到的问题还不少,链接:http://blog.
1144 0
|
Linux
[20171205]奇怪的文件名.txt
[20171205]奇怪的文件名.txt --//今天在服务器发现在目录/usr/share/man/存在一个文件名. # ls -lb /usr/share/man/ | head total 932 -rw-r--r--  1 root root...
1084 0
|
SQL Oracle 关系型数据库
[20170918]exp 直接路径导出.txt
[20170918]exp 直接路径导出.txt --//昨天看链接blogs.oracle.com/database4cn/%e5%af%b9%e4%ba%8e%e4%b8%80%e4%b8%aa%e9%9d%9e%e7%a9%ba%e5%ad%97%e6%ae%...
1486 0
|
SQL Oracle 关系型数据库
[20170112]为什么提示不一样.txt
[20170112]为什么提示不一样.txt --问题链接:http://www.itpub.net/thread-2074643-1-1.html --自己也测试看看: 1.
867 0
|
Oracle 关系型数据库 Shell
[20161130]快速进入目录.txt
[20161130]快速进入目录.txt --要建立如下相似链接,便于快速访问 adump -> /u01/app/oracle/admin/${ORACLE_SID}/adump bdump -> /u01/app/oracle/diag/rdbms/$...
750 0
|
运维 Oracle 关系型数据库
[20160604]浅谈出错提示.txt
[20160604]浅谈出错提示.txt --这个问题主要由于上个礼拜正常上班时间遇到的问题,导致整个业务停顿10分钟上下,出错提示"解析URL错误",实际上这个问题我遇到过 --一次,当时不是我解决的,我提交软件组,我记得对方提到1台服务器服务出现问题,重启服务就ok了.
1191 0
|
关系型数据库 Oracle 测试技术
[20151231]空文件.txt
[20151231]空文件.txt --记录上午工作中一个小错误,忘记存盘,直接调用出现的问题,通过例子来说明: 1.测试环境: SCOTT@test01p> @ ver1 PORT_STRING                    VERSION     ...
612 0