首页 > 其他分享 >[20230903]执行计划ANTI SNA和ANTI NA表示什么.txt

[20230903]执行计划ANTI SNA和ANTI NA表示什么.txt

时间:2023-09-04 20:44:19浏览次数:62  
标签:00 txt T1 SNA ID ANTI 00.01 SEL id

[20230903]执行计划ANTI SNA和ANTI NA表示什么.txt

--//在not in的sql语句什么出现ANTI SNA或者ANTI NA(注:不会出现在not exists语句中),我自己是非常混乱的。
--//我看了以前的链接 http://blog.itpub.net/267265/viewspace-2157424/ =>[20180705]关于hash join 2.txt
--//还是发现一些自己以前的错误,尝试做一些解析,也许还是不对。

--//首先ANTI 表示反。Aware表示意识清楚的意思。single 表示单边。
--//NA =>  Null-Aware.
--//SNA => Single Null-Aware

--//官方的解析如下:
The ANTI SNA stands for "single null-aware antijoin." ANTI NA stands for "null-aware antijoin." The null-aware operation
enables the optimizer to use the antijoin optimization even on a nullable column. In releases earlier than Oracle
Database 11g, the database could not perform antijoins on NOT IN queries when nulls were possible.

--//我自己开始的理解既然single表示单边的意思,就是仅仅1边做Null-Aware。这样Null-Aware最开始的理解就是两边,但是我以前的
--//测试把自己带偏了,还是通过例子说明问题。

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 ( id number,name varchar2(10));
create table t2 ( id number,name varchar2(10));

insert into t1 values (null,'t1null');
insert into t2 values (null,'t2null');

insert into t1 select level id   ,'t1'||to_char(level) name from dual connect by level<=4;
insert into t2 select level+1 id ,'t2'||to_char(level+1) name from dual connect by level<=4;

commit ;
--//分析略.

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

SCOTT@test01p> select * from t1 where id not in (select id from t2 ) ;
no rows selected

Plan hash value: 1275484728

------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |     6 (100)|          |      0 |00:00:00.01 |      13 |       |       |          |
|*  1 |  HASH JOIN ANTI NA |      |      1 |      2 |    22 |     6   (0)| 00:00:01 |      0 |00:00:00.01 |      13 |  1995K|  1995K| 1053K (0)|
|   2 |   TABLE ACCESS FULL| T1   |      1 |      5 |    40 |     3   (0)| 00:00:01 |      5 |00:00:00.01 |       7 |       |       |          |
|   3 |   TABLE ACCESS FULL| T2   |      1 |      5 |    15 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       6 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
--//出现的是HASH JOIN ANTI NA,所以我当时第1个感觉两个表都做Null-Aware。但是当我执行如下:

SCOTT@test01p> select * from t1 where id not in (select id from t2 ) and t1.id is not null;
no rows selected

Plan hash value: 1275484728
------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |     6 (100)|          |      0 |00:00:00.01 |      13 |       |       |          |
|*  1 |  HASH JOIN ANTI NA |      |      1 |      1 |    11 |     6   (0)| 00:00:01 |      0 |00:00:00.01 |      13 |  1995K|  1995K| 1053K (0)|
|*  2 |   TABLE ACCESS FULL| T1   |      1 |      4 |    32 |     3   (0)| 00:00:01 |      4 |00:00:00.01 |       7 |       |       |          |
|   3 |   TABLE ACCESS FULL| T2   |      1 |      5 |    15 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       6 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
   2 - filter("T1"."ID" IS NOT NULL)
--//出现的还是HASH JOIN ANTI NA,我当时的理解以为会出现SNA,仅仅在id=2,加入了过滤条件("T1"."ID" IS NOT NULL)。
--//似乎给我的感觉是NA仅仅作用于t2表。
--//补充:
SCOTT@test01p> select /*+ SWAP_JOIN_INPUTS(@SEL$5DA710D3 , T2@SEL$2) */ * from t1 where id not in (select id from t2 ) and t1.id is not null;
no rows selected

Plan hash value: 2739594415
-----------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |     6 (100)|          |      0 |00:00:00.01 |       6 |       |       |          |
|*  1 |  HASH JOIN RIGHT ANTI NA|      |      1 |      1 |    11 |     6   (0)| 00:00:01 |      0 |00:00:00.01 |       6 |  1995K|  1995K|  458K (0)|
|   2 |   TABLE ACCESS FULL     | T2   |      1 |      5 |    15 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       6 |       |       |          |
|*  3 |   TABLE ACCESS FULL     | T1   |      0 |      4 |    32 |     3   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |       |       |          |
-----------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T2@SEL$2
   3 - SEL$5DA710D3 / T1@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
   3 - filter("T1"."ID" IS NOT NULL)
--//可以发现如果t2.id 存在null,根本没有执行id=3,t1表的全表扫描操作(starts=0)。
--//而当我执行如下:

SCOTT@test01p> select * from t1 where id not in (select id from t2 where t2.id is not null) ;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 1270581391
------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |     6 (100)|          |      1 |00:00:00.01 |      14 |       |       |          |
|*  1 |  HASH JOIN ANTI SNA|      |      1 |      2 |    22 |     6   (0)| 00:00:01 |      1 |00:00:00.01 |      14 |  1995K|  1995K| 1054K (0)|
|   2 |   TABLE ACCESS FULL| T1   |      1 |      5 |    40 |     3   (0)| 00:00:01 |      5 |00:00:00.01 |       7 |       |       |          |
|*  3 |   TABLE ACCESS FULL| T2   |      1 |      4 |    12 |     3   (0)| 00:00:01 |      4 |00:00:00.01 |       7 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
   3 - filter("T2"."ID" IS NOT NULL)

--//出现的是HASH JOIN ANTI SNA,很明显输出排除了t1.id is null的输出,相当于加入条件t1.id is not null.
--//所以当时我推导出了一个结论,对于前面的例子sna作用于表t1,na作用于表T2(而且是反了的,不好表述),注当时连接使用左右连接表
--//述有点不合适。

3.而实际的情况,看看存在索引的情况;

SCOTT@test01p> create index i_t1_id on t1(id);
Index created.

SCOTT@test01p> create index i_t2_id on t2(id);
Index created.

SCOTT@test01p> select * from t1 where id not in (select id from t2 ) and t1.id is not null;
no rows selected

Plan hash value: 1336658214
-------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name    | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |         |      1 |        |       |     5 (100)|          |      0 |00:00:00.01 |       6 |
|*  1 |  FILTER                               |         |      1 |        |       |            |          |      0 |00:00:00.01 |       6 |
|   2 |   NESTED LOOPS ANTI SNA               |         |      0 |      1 |    11 |     5  (60)| 00:00:01 |      0 |00:00:00.01 |       0 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T1      |      0 |      4 |    32 |     2   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|*  4 |     INDEX FULL SCAN                   | I_T1_ID |      0 |      4 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|*  5 |    INDEX RANGE SCAN                   | I_T2_ID |      0 |      5 |    15 |     0   (0)|          |      0 |00:00:00.01 |       0 |
|*  6 |   TABLE ACCESS FULL                   | T2      |      1 |      1 |     3 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |       6 |
-------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T1@SEL$1
   5 - SEL$5DA710D3 / T2@SEL$2
   6 - SEL$2        / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NULL)
   4 - filter("T1"."ID" IS NOT NULL)
   5 - access("ID"="ID")
   6 - filter("ID" IS NULL)
--//出现了NESTED LOOPS ANTI SNA,而前面没有索引时是HASH JOIN ANTI NA。
--//这里的ANTI SNA,明显作用T2,注意看id=6的过滤条件是filter("ID" IS NULL)。
--//另外显示的执行计划有点问题,按照以前读执行计划的执行顺序应该是(id)4->3->5->2->6->1.
--//而实际的执行情况是6->4->3->5->2->1. 当id=6的输出存在id is null时,id=2,3,4,5不会执行。

delete t2 where id is null and name ='t2null';
commit;

SCOTT@test01p> select * from t1 where id not in (select  id from t2 ) and t1.id is not null;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 1336658214
-------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name    | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |         |      1 |        |       |     5 (100)|          |      1 |00:00:00.01 |      13 |
|*  1 |  FILTER                               |         |      1 |        |       |            |          |      1 |00:00:00.01 |      13 |
|   2 |   NESTED LOOPS ANTI SNA               |         |      1 |      1 |    11 |     5  (60)| 00:00:01 |      1 |00:00:00.01 |       6 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T1      |      1 |      4 |    32 |     2   (0)| 00:00:01 |      4 |00:00:00.01 |       4 |
|*  4 |     INDEX FULL SCAN                   | I_T1_ID |      1 |      4 |       |     1   (0)| 00:00:01 |      4 |00:00:00.01 |       2 |
|*  5 |    INDEX RANGE SCAN                   | I_T2_ID |      4 |      5 |    15 |     0   (0)|          |      3 |00:00:00.01 |       2 |
|*  6 |   TABLE ACCESS FULL                   | T2      |      1 |      1 |     3 |     3   (0)| 00:00:01 |      0 |00:00:00.01 |       7 |
-------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T1@SEL$1
   5 - SEL$5DA710D3 / T2@SEL$2
   6 - SEL$2        / 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$2")
      OUTLINE_LEAF(@"SEL$5DA710D3")
      UNNEST(@"SEL$2")
      OUTLINE(@"SEL$1")
      OUTLINE(@"SEL$2")
      INDEX(@"SEL$5DA710D3" "T1"@"SEL$1" ("T1"."ID"))
      BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$5DA710D3" "T1"@"SEL$1")
      INDEX(@"SEL$5DA710D3" "T2"@"SEL$2" ("T2"."ID"))
      LEADING(@"SEL$5DA710D3" "T1"@"SEL$1" "T2"@"SEL$2")
      USE_NL(@"SEL$5DA710D3" "T2"@"SEL$2")
      FULL(@"SEL$2" "T2"@"SEL$2")
      END_OUTLINE_DATA
  */
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NULL)
   4 - filter("T1"."ID" IS NOT NULL)
   5 - access("ID"="ID")
   6 - filter("ID" IS NULL)
--//当id=6的查询id is null为0条时,id=2,3,4,5才会执行。注意看id=6的A-Rows=0.

insert into t2  select rownum+5 ,'t2'||to_char(rownum+5) from dual connect by level<=10000;
insert into t2 values (null,'T2NULL');
commit;

--//注:不要分析表T2,不然执行计划会变。我的测试发现分析即使加入上面的outline也不会选择原来的执行计划。
--//这个问题放在后面再看看。

SCOTT@test01p> select * from t1 where id not in (select  id from t2 ) and t1.id is not null;
no rows selected

Plan hash value: 1336658214
-------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name    | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |         |      1 |        |       |     5 (100)|          |      0 |00:00:00.01 |      24 |
|*  1 |  FILTER                               |         |      1 |        |       |            |          |      0 |00:00:00.01 |      24 |
|   2 |   NESTED LOOPS ANTI SNA               |         |      0 |      1 |    11 |     5  (60)| 00:00:01 |      0 |00:00:00.01 |       0 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T1      |      0 |      4 |    32 |     2   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|*  4 |     INDEX FULL SCAN                   | I_T1_ID |      0 |      4 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|*  5 |    INDEX RANGE SCAN                   | I_T2_ID |      0 |      5 |    15 |     0   (0)|          |      0 |00:00:00.01 |       0 |
|*  6 |   TABLE ACCESS FULL                   | T2      |      1 |      1 |     3 |     3   (0)| 00:00:01 |      1 |00:00:00.01 |      24 |
-------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T1@SEL$1
   5 - SEL$5DA710D3 / T2@SEL$2
   6 - SEL$2        / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NULL)
   4 - filter("T1"."ID" IS NOT NULL)
   5 - access("ID"="ID")
   6 - filter("ID" IS NULL)
--//我插入表t2的id is null值在最后,导致id=6的逻辑读增加到24。
--//像这类语句如何优化呢?
--//方法1如果表t2的id确实不存在null值,可以加入约束限制id is not null。这样执行计划不会再出现NA或者SNA.
--//方法2如果表t2的id确实存在null值,可以建立包含null值的函数索引看看。先尝试建立函数索引的情况。

SCOTT@test01p> create index if_t2_id on t2(id,0);
Index created.

SCOTT@test01p> insert into t2 values (null,'t2null');
1 row created.

SCOTT@test01p> commit;
Commit complete.

SCOTT@test01p> select count(*) from t2 where id is  null;
  COUNT(*)
----------
         2
--//表T2存在2条id is null。

SCOTT@test01p> select /*+  index(@"SEL$2" "T2"@"SEL$2") */ * from t1 where id not in (select  id from t2 ) and t1.id is not null;
no rows selected

Plan hash value: 894503773
--------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |          |      1 |        |       |     8 (100)|          |      0 |00:00:00.01 |       2 |
|*  1 |  FILTER                               |          |      1 |        |       |            |          |      0 |00:00:00.01 |       2 |
|   2 |   NESTED LOOPS ANTI SNA               |          |      0 |      1 |    11 |     8  (75)| 00:00:01 |      0 |00:00:00.01 |       0 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T1       |      0 |      4 |    32 |     2   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|*  4 |     INDEX FULL SCAN                   | I_T1_ID  |      0 |      4 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|*  5 |    INDEX RANGE SCAN                   | I_T2_ID  |      0 |      5 |    15 |     0   (0)|          |      0 |00:00:00.01 |       0 |
|*  6 |   INDEX RANGE SCAN                    | IF_T2_ID |      1 |      1 |     3 |     6   (0)| 00:00:01 |      1 |00:00:00.01 |       2 |
--------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T1@SEL$1
   5 - SEL$5DA710D3 / T2@SEL$2
   6 - SEL$2        / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NULL)
   4 - filter("T1"."ID" IS NOT NULL)
   5 - access("ID"="ID")
   6 - access("ID" IS NULL)
--//id=6,仅仅取1条就ok了。

SCOTT@test01p> delete from t2 where id is  null;
2 rows deleted.

SCOTT@test01p> commit;
Commit complete.

SCOTT@test01p> alter table t2 modify(id  not null);
Table altered.

SCOTT@test01p> select * from t1 where id not in (select  id from t2 ) and t1.id is not null;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 1662492778
------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name    | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |         |      1 |        |       |     2 (100)|          |      1 |00:00:00.01 |      10 |
|   1 |  NESTED LOOPS ANTI                   |         |      1 |      1 |    11 |     2   (0)| 00:00:01 |      1 |00:00:00.01 |      10 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T1      |      1 |      4 |    32 |     2   (0)| 00:00:01 |      4 |00:00:00.01 |       4 |
|*  3 |    INDEX FULL SCAN                   | I_T1_ID |      1 |      4 |       |     1   (0)| 00:00:01 |      4 |00:00:00.01 |       2 |
|*  4 |   INDEX RANGE SCAN                   | I_T2_ID |      4 |      4 |    12 |     0   (0)|          |      3 |00:00:00.01 |       6 |
------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("T1"."ID" IS NOT NULL)
   4 - access("ID"="ID")
--//执行计划变了简单许多,不再出现ANTI SNA或者ANTI NA。

SCOTT@test01p> alter table t2 modify(id   null);
Table altered.

SCOTT@test01p> drop index IF_T2_ID;
Index dropped.

--//分析表T2略!!

SCOTT@test01p> select * from t1 where id not in (select  id from t2 ) and t1.id is not null;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 4053963239
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |    11 (100)|          |      1 |00:00:00.01 |      33 |       |       |          |
|*  1 |  HASH JOIN ANTI NA                   |         |      1 |      1 |    12 |    11   (0)| 00:00:01 |      1 |00:00:00.01 |      33 |  1995K|  1995K| 1044K (0)|
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T1      |      1 |      4 |    32 |     2   (0)| 00:00:01 |      4 |00:00:00.01 |       2 |       |       |          |
|*  3 |    INDEX FULL SCAN                   | I_T1_ID |      1 |      4 |       |     1   (0)| 00:00:01 |      4 |00:00:00.01 |       1 |       |       |          |
|   4 |   TABLE ACCESS FULL                  | T2      |      1 |  10004 | 40016 |     9   (0)| 00:00:01 |  10004 |00:00:00.01 |      31 |       |       |          |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
   3 - filter("T1"."ID" IS NOT NULL)
--//这样的执行计划很难看出要建立函数索引包含null值,解决语句的优化问题。

select
/*+
    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$2")
    OUTLINE_LEAF(@"SEL$5DA710D3")
    UNNEST(@"SEL$2")
    OUTLINE(@"SEL$1")
    OUTLINE(@"SEL$2")
    INDEX(@"SEL$5DA710D3" "T1"@"SEL$1" ("T1"."ID"))
    BATCH_TABLE_ACCESS_BY_ROWID(@"SEL$5DA710D3" "T1"@"SEL$1")
    INDEX(@"SEL$5DA710D3" "T2"@"SEL$2" ("T2"."ID"))
    LEADING(@"SEL$5DA710D3" "T1"@"SEL$1" "T2"@"SEL$2")
    USE_NL(@"SEL$5DA710D3" "T2"@"SEL$2")
    FULL(@"SEL$2" "T2"@"SEL$2")
    END_OUTLINE_DATA
*/
* from t1 where id not in (select  id from t2 ) and t1.id is not null;  

Plan hash value: 4053963239
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |    11 (100)|          |      1 |00:00:00.01 |      33 |       |       |          |
|*  1 |  HASH JOIN ANTI NA                   |         |      1 |      1 |    12 |    11   (0)| 00:00:01 |      1 |00:00:00.01 |      33 |  1995K|  1995K| 1044K (0)|
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T1      |      1 |      4 |    32 |     2   (0)| 00:00:01 |      4 |00:00:00.01 |       2 |       |       |          |
|*  3 |    INDEX FULL SCAN                   | I_T1_ID |      1 |      4 |       |     1   (0)| 00:00:01 |      4 |00:00:00.01 |       1 |       |       |          |
|   4 |   TABLE ACCESS FULL                  | T2      |      1 |  10004 | 40016 |     9   (0)| 00:00:01 |  10004 |00:00:00.01 |      31 |       |       |          |
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
   3 - filter("T1"."ID" IS NOT NULL)
--//加入提示不起作用。

SCOTT@test01p> create index if_t2_id on t2(id,0);
Index created.

SCOTT@test01p> drop index I_T2_ID;
Index dropped.

SCOTT@test01p> select * from t1 where id not in (select  id from t2 ) and t1.id is not null;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 2867236616
-----------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers | Reads  |
-----------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |          |      1 |        |       |     7 (100)|          |      1 |00:00:00.01 |      12 |      1 |
|*  1 |  FILTER                               |          |      1 |        |       |            |          |      1 |00:00:00.01 |      12 |      1 |
|   2 |   NESTED LOOPS ANTI SNA               |          |      1 |      1 |    12 |     7  (15)| 00:00:01 |      1 |00:00:00.01 |      10 |      1 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T1       |      1 |      4 |    32 |     2   (0)| 00:00:01 |      4 |00:00:00.01 |       4 |      0 |
|*  4 |     INDEX FULL SCAN                   | I_T1_ID  |      1 |      4 |       |     1   (0)| 00:00:01 |      4 |00:00:00.01 |       2 |      0 |
|*  5 |    INDEX RANGE SCAN                   | IF_T2_ID |      4 |   9170 | 36680 |     1   (0)| 00:00:01 |      3 |00:00:00.01 |       6 |      1 |
|*  6 |   INDEX RANGE SCAN                    | IF_T2_ID |      1 |      1 |     4 |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       2 |      0 |
-----------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T1@SEL$1
   5 - SEL$5DA710D3 / T2@SEL$2
   6 - SEL$2        / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter( IS NULL)
   4 - filter("T1"."ID" IS NOT NULL)
   5 - access("ID"="ID")
   6 - access("ID" IS NULL)
--//仅仅给出一个优化思路,出现这样的情况可以考虑建立1个函数索引,避免全表扫描。

3.总结:
--//1.还是无法搞清在not in的sql语句什么出现ANTI SNA或者ANTI NA,至少说明一点sna作用于一边。
--//2.注意NULL判断,NULL的逻辑仅仅存在 NULL is NULL 是true,NULL is not NULL是false,其它NULL=NULL之类的都表示noknown.
--//3.注意仅仅not in在执行计划中才存在NA,SNA. not exists 不会
--//4.个人建议,使用exists/not exists代替in/not in,可以满足大部分业务与实际查询的需要.甚至可以建议开发应该忘记not in的写法。
--//即使使用not exists也要注意,在实际工作中许多可以使用exists代替,我以前就遇到类似的情况。开发思维是反的,查询里面包含
--//1个状态位,就2个值。完全可以改写成exists,可以很好利用状态位的索引,许多情况下exists的执行比not exists成本要低许多。
--//5.适当给字段加入not null约束,规避一些这样的情况的全表扫描,因为全NULL值,在oracle中的常规索引不记录,导致无法使用索引.
--//也引出另外的优化解决方法,就是建立适当的函数索引。
--//6.以上完全是基于测试的猜测,不知道是否正确.

4.补充测试:
SCOTT@test01p> drop index I_T1_ID;
Index dropped.

SCOTT@test01p> create index if_t1_id on t1(id,0);
Index created.

SCOTT@test01p> select * from t1 where id not in (select  id from t2 where t2.id is not null) ;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 1968750447
-------------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |    11 (100)|          |      1 |00:00:00.01 |      37 |       |       |          |
|*  1 |  HASH JOIN ANTI SNA   |          |      1 |      2 |    24 |    11   (0)| 00:00:01 |      1 |00:00:00.01 |      37 |  1995K|  1995K|  996K (0)|
|   2 |   TABLE ACCESS FULL   | T1       |      1 |      5 |    40 |     3   (0)| 00:00:01 |      5 |00:00:00.01 |       7 |       |       |          |
|*  3 |   INDEX FAST FULL SCAN| IF_T2_ID |      1 |  10004 | 40016 |     8   (0)| 00:00:01 |  10004 |00:00:00.01 |      30 |       |       |          |
-------------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T2@SEL$2

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
   3 - filter("T2"."ID" IS NOT NULL)
--//这里的sna作用于T1.

SCOTT@test01p> select /*+ index(t1) */ * from t1 where id not in (select  id from t2 where t2.id is not null) ;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 1750879247
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 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 |        |       |    10 (100)|          |      1 |00:00:00.01 |      32 |       |       |          |
|*  1 |  HASH JOIN ANTI SNA                  |          |      1 |      2 |    24 |    10   (0)| 00:00:01 |      1 |00:00:00.01 |      32 |  1995K|  1995K|  992K (0)|
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T1       |      1 |      5 |    40 |     2   (0)| 00:00:01 |      5 |00:00:00.01 |       2 |       |       |          |
|   3 |    INDEX FULL SCAN                   | IF_T1_ID |      1 |      5 |       |     1   (0)| 00:00:01 |      5 |00:00:00.01 |       1 |       |       |          |
|*  4 |   INDEX FAST FULL SCAN               | IF_T2_ID |      1 |  10004 | 40016 |     8   (0)| 00:00:01 |  10004 |00:00:00.01 |      30 |       |       |          |
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("ID"="ID")
   4 - filter("T2"."ID" IS NOT NULL)
--//这样的执行方式逻辑读有点高.无论我如何加提示,都无法把逻辑读降下来。只能通过约束看看,前提是t1.id没有null值。

SCOTT@test01p> delete from t1 where id is  null;
1 row deleted.

SCOTT@test01p> commit;
Commit complete.

SCOTT@test01p> alter table t1 modify(id not null);
Table altered.

SCOTT@test01p> select * from t1 where id not in (select  id from t2 where t2.id is not null) ;
        ID NAME
---------- --------------------
         1 t11

SCOTT@test01p> @ dpc '' '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  5cv4tdffbwrjn, child number 0
-------------------------------------
select * from t1 where id not in (select  id from t2 where t2.id is not
null)
Plan hash value: 4102018781
-------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |          |      1 |        |       |     8 (100)|          |      1 |00:00:00.01 |      14 |
|   1 |  NESTED LOOPS ANTI |          |      1 |      2 |    24 |     8   (0)| 00:00:01 |      1 |00:00:00.01 |      14 |
|   2 |   TABLE ACCESS FULL| T1       |      1 |      5 |    40 |     3   (0)| 00:00:01 |      4 |00:00:00.01 |       8 |
|*  3 |   INDEX RANGE SCAN | IF_T2_ID |      4 |   6669 | 26676 |     1   (0)| 00:00:01 |      3 |00:00:00.01 |       6 |
-------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("ID"="ID")
       filter("T2"."ID" IS NOT NULL)

SCOTT@test01p> select /*+ index(t1) */ * from t1 where id not in (select  id from t2 where t2.id is not null) ;
        ID NAME
---------- --------------------
         1 t11

Plan hash value: 1026320075
-------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |          |      1 |        |       |     7 (100)|          |      1 |00:00:00.01 |      10 |
|   1 |  NESTED LOOPS ANTI                   |          |      1 |      2 |    24 |     7   (0)| 00:00:01 |      1 |00:00:00.01 |      10 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T1       |      1 |      5 |    40 |     2   (0)| 00:00:01 |      4 |00:00:00.01 |       4 |
|   3 |    INDEX FULL SCAN                   | IF_T1_ID |      1 |      5 |       |     1   (0)| 00:00:01 |      4 |00:00:00.01 |       2 |
|*  4 |   INDEX RANGE SCAN                   | IF_T2_ID |      4 |   6669 | 26676 |     1   (0)| 00:00:01 |      3 |00:00:00.01 |       6 |
-------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T1@SEL$1
   4 - SEL$5DA710D3 / T2@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("ID"="ID")
       filter("T2"."ID" IS NOT NULL)

标签:00,txt,T1,SNA,ID,ANTI,00.01,SEL,id
From: https://www.cnblogs.com/lfree/p/17678047.html

相关文章

  • 使用百度文心一言生成bat代码(查找当前目录所有文件名带11的txt文件并修改为22)
    百度文心一言开放给普通用户了,网址:文心一言请用[bat]编程:查找当前目录所有文件名带11的txt文件并修改为22生成的代码需要简单修改下,主要问题在->位置没有使用转义符会导致echo后面的字符到txt中,思路还是可以的,不错点击查看代码@echooffsetlocalenabledelayedexpansion......
  • iptables、共享上网SNAT、端口转发DNAT
    iptables、共享上网SNAT、端口转发DNAT1.防火墙概述封端⼝,封ip实现NAT功能共享上⽹端⼝映射(端⼝转发),ip映射2.防火墙2.1防火墙种类以及使用说明硬件:整个企业入口软件:开源软件网站内部封ipiptables云防火墙安全组NAT网关waf应用防火墙2.2专有名词容器、表、链......
  • dotnet SemanticKernel 入门 调用原生本机技能
    本文将告诉大家如何在SemanticKernel里面调用原生本机技能,所谓原生本机技能就是使用C#代码编写的原生本地逻辑技能,这里的技能可讲的可不是游戏角色里面的技能哈,指的是实现某个功能的技能,这是构成AI强大能力的基础本文属于SemanticKernel入门系列博客,更多博客内容请参阅我......
  • dotnet SemanticKernel 入门 将技能导入框架
    在上一篇博客中和大家简单介绍了SemanticKernel里的技能概念,接下来咱准备将技能导入到SemanticKernel框架里面,进行一个管道式调用本文属于SemanticKernel入门系列博客,更多博客内容请参阅我的博客导航别着急,本篇博客还不涉及到任何的GPT相关的魔法,仅仅只是在C#层面......
  • dotnet SemanticKernel 入门 注入日志
    使用SemanticKernel框架在对接AI时,由于使用到了大量的魔法,需要有日志的帮助才好更方便定位问题,本文将告诉大家如何在SemanticKernel注入日志本文属于SemanticKernel入门系列博客,更多博客内容请参阅我的博客导航在KernelBuilder创建器里面可以通过WithLogger注入IL......
  • dotnet SemanticKernel 入门 自定义变量和技能
    本文将告诉大家如何在SemanticKernel框架内定义自定义的变量和如何开发自定义的技能本文属于SemanticKernel入门系列博客,更多博客内容请参阅我的博客导航自定义变量是一个非常有用的技能,自定义变量可以让炼丹师和程序员进行并行工作。由炼丹师对AI模型进行训练,从而找到对......
  • [20230826]dc命令复杂学习2.txt
    [20230826]dc命令复杂学习2.txt--//昨天做了累加的例子,并解析命令里面的意思.今天尝试做一个阶乘的例子.$seq5|dc-f--e"[*z1<r]srz1<rp"120--//很简单就是里面的+换成了*,实际上我使用seq5传了5个参数.如果传入1个呢?--//假设做10的阶乘.$echo10*9*8*7*6*5*4*3*2*1|......
  • 【python】使用ddddocr模块报错处理:AttributeError: module 'PIL.Image' has no attri
    安装pipinstallddddocr安装特别慢,几kb每秒,而且容易超时报错使用清华源下载:pipinstall-ihttps://pypi.tuna.tsinghua.edu.cn/simpleddddocr使用img_url="https://user.wangxiao.cn/apis//common/getImageCaptcha"img_resp=session.post(img_url)......
  • Python使用jieba分词输出txt文件
    1、准备好一个原始的txt文件2、在Pycharm里面下载jieba包3、将txt文件放到项目里面hlm.txt为原始文件;result.txt为存放结果的文件;4、分词代码importjiebaarticle=open('hlm.txt','r',encoding='UTF-8')sent=article.read()sent_list=jieba.cut(sent)resu......
  • [20230825]dc命令复杂学习.txt
    [20230825]dc命令复杂学习.txt--//前几天学习dc使用,我当时最后举了一个累加的例子,里面--//-e后面那一串什么意思,即使看了mandc文档,我当时也没看懂表示什么意思.尝试看了man文档,简单解析如下:--//我从文档里面取出相关说明:[characters]Makesastringcontainingcharacters......