[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一次,并且因为很快定位.逻辑读很小.

目录
相关文章
|
Python
Python基础-常见问题:访问文件时,提示“PermissionError: [WinError 5] 拒绝访问。: ‘test.txt‘”,怎么办呢?
Python基础-常见问题:访问文件时,提示“PermissionError: [WinError 5] 拒绝访问。: ‘test.txt‘”,怎么办呢?
689 0
将cmd中命令输出保存为TXT文本文件
转自:https://www.cnblogs.com/hongten/archive/2013/03/27/hongten_windows_cms.html 例如:将Ping命令的加长包输出到D盘的ping.txt文本文件。
1544 0
|
Linux
[20171205]奇怪的文件名.txt
[20171205]奇怪的文件名.txt --//今天在服务器发现在目录/usr/share/man/存在一个文件名. # ls -lb /usr/share/man/ | head total 932 -rw-r--r--  1 root root...
1044 0
|
Oracle 关系型数据库 数据库
[20170628]11g修改用户名.txt
[20170628]11g修改用户名.txt --//昨天看了链接,提到修改用户名: http://www.oratea.com/2017/06/26/oracle-11g%e4%bf%ae%e6%94%b9%e7%94%a8%e6%88%b7%e5%90%8d/ --//自己也测试看看.
944 0
|
SQL Oracle 关系型数据库
[20170112]为什么提示不一样.txt
[20170112]为什么提示不一样.txt --问题链接:http://www.itpub.net/thread-2074643-1-1.html --自己也测试看看: 1.
835 0
|
运维 Oracle 关系型数据库
[20160604]浅谈出错提示.txt
[20160604]浅谈出错提示.txt --这个问题主要由于上个礼拜正常上班时间遇到的问题,导致整个业务停顿10分钟上下,出错提示"解析URL错误",实际上这个问题我遇到过 --一次,当时不是我解决的,我提交软件组,我记得对方提到1台服务器服务出现问题,重启服务就ok了.
1149 0
|
关系型数据库 Oracle 测试技术
[20151231]空文件.txt
[20151231]空文件.txt --记录上午工作中一个小错误,忘记存盘,直接调用出现的问题,通过例子来说明: 1.测试环境: SCOTT@test01p> @ ver1 PORT_STRING                    VERSION     ...
592 0
|
SQL Windows
[20151210]oerr显示错误信息.txt
[20151210]oerr显示错误信息.txt --以前写过1篇windows下使用oerr的方法,实际上12c已经自带,我现在使用client端是12c,但是没有oraus.msg文件,从别的os 拷贝过 --来就可以使用。
869 0