oracle中not exists对外层查询的影响

又一个类似『12c比10g索引回表消耗增多的问题』的案例,同事在12c中跑的buffer get很高,但是在10g中跑的buffer很低。怀疑是不是12c的优化器有问题。

这个10g的环境和12c的环境,数据量大致一样,只是有很少部分的不同,但是就是这个很少部分不同,造成了not exists中的子查询返回不同的值,进而对外层查询产生不同的影响。

我们来用如下的代码模拟一下。

初始化数据:
--10g
drop table t1;
drop table t2;
 
create table t1 (id number,name varchar2(20),dep_id varchar2(10));
create table t2 (id number,name varchar2(20),dep_id varchar2(10));
 
insert into t1 select rownum,'a','kk' from dual connect by level <=3000000;
insert into t2 select rownum,'a','kk' from dual connect by level <=1000000;
insert into t2 select rownum,'a','mm' from dual;
 
commit;
 
 
--12c
drop table t1;
drop table t2;
 
create table t1 (id number,name varchar2(20),dep_id varchar2(10));
create table t2 (id number,name varchar2(20),dep_id varchar2(10));
 
 
insert into t1 select rownum,'a','kk' from dual connect by level <=3000000;
insert into t2 select rownum,'a','kk' from dual connect by level <=1000000;
 
commit;
我们看到,12c的数据和10g只是有很少的差别,t1表12c和10g都一样,t2表在12c只是少了一行数据。

--10g
SQL> select dep_id,count(*) from t1 group by dep_id;
 
DEP_ID                 COUNT(*)
-------------------- ----------
kk                      3000000
 
SQL> select dep_id,count(*) from t2 group by dep_id;
 
DEP_ID                 COUNT(*)
-------------------- ----------
mm                            1
kk                      1000000
 
SQL>
 
 
--12c
SQL> select dep_id,count(*) from t1 group by dep_id;
 
DEP_ID                 COUNT(*)
-------------------- ----------
kk                      3000000
 
SQL> select dep_id,count(*) from t2 group by dep_id;
 
DEP_ID                 COUNT(*)
-------------------- ----------
kk                      1000000
 
SQL>

我们将要执行的sql语句是:

select count(*)
  from t1, t2
 where t1.id = t2.id
   and t1.dep_id = 'kk'
   and not exists (select 1
          from t1, t2
         where t1.id = t2.id
           and t2.dep_id = 'mm');
我们先来看执行情况的差距,10g的bufferget小,12c多:

--10g
SQL> select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm');

  COUNT(*)
----------
         0

SQL> select* from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  22t5mb43w55pr, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not
exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm')

Plan hash value: 3404612428

------------------------------------------------------------------------------------------------------------------
| Id  | Operation            | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |      1 |        |      1 |00:00:00.02 |    2086 |       |       |          |
|   1 |  SORT AGGREGATE      |      |      1 |      1 |      1 |00:00:00.02 |    2086 |       |       |          |
|*  2 |   FILTER             |      |      1 |        |      0 |00:00:00.02 |    2086 |       |       |          |
|*  3 |    HASH JOIN         |      |      0 |    901K|      0 |00:00:00.01 |       0 |    39M|  5518K|          |
|   4 |     TABLE ACCESS FULL| T2   |      0 |    901K|      0 |00:00:00.01 |       0 |       |       |          |
|*  5 |     TABLE ACCESS FULL| T1   |      0 |   2555K|      0 |00:00:00.01 |       0 |       |       |          |
|*  6 |    HASH JOIN         |      |      1 |     23 |      1 |00:00:00.02 |    2086 |  1517K|  1517K|  612K (0)|
|*  7 |     TABLE ACCESS FULL| T2   |      1 |     23 |      1 |00:00:00.02 |    2082 |       |       |          |
|   8 |     TABLE ACCESS FULL| T1   |      1 |   2555K|      1 |00:00:00.01 |       4 |       |       |          |
------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter( IS NULL)
   3 - access("T1"."ID"="T2"."ID")
   5 - filter("T1"."DEP_ID"='kk')
   6 - access("T1"."ID"="T2"."ID")
   7 - filter("T2"."DEP_ID"='mm')

Note
-----
   - dynamic sampling used for this statement

34 rows selected.

SQL>

--12c
SQL> select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm');

  COUNT(*)
----------
   1000000

SQL> select* from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  22t5mb43w55pr, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t1,t2 where
t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2
where t1.id=t2.id and t2.dep_id='mm')

Plan hash value: 1692274438

--------------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |      1 |        |      1 |00:00:00.79 |   10662 |       |    |     |
|   1 |  SORT AGGREGATE        |      |      1 |      1 |      1 |00:00:00.79 |   10662 |       |    |     |
|*  2 |   FILTER               |      |      1 |        |   1000K|00:00:00.74 |   10662 |       |    |     |
|*  3 |    HASH JOIN           |      |      1 |   1215K|   1000K|00:00:00.52 |    8579 |    43M|  6111K|   42M (0)|
|   4 |     TABLE ACCESS FULL  | T2   |      1 |   1215K|   1000K|00:00:00.01 |    2083 |       |    |     |
|*  5 |     TABLE ACCESS FULL  | T1   |      1 |   2738K|   3000K|00:00:00.07 |    6496 |       |    |     |
|*  6 |    HASH JOIN RIGHT SEMI|      |      1 |     35 |      0 |00:00:00.02 |    2083 |  1245K|  1245K|  461K (0)|
|*  7 |     TABLE ACCESS FULL  | T2   |      1 |     23 |      0 |00:00:00.02 |    2083 |       |    |     |
|   8 |     TABLE ACCESS FULL  | T1   |      0 |   2738K|      0 |00:00:00.01 |       0 |       |    |     |
--------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter( IS NULL)
   3 - access("T1"."ID"="T2"."ID")
   5 - filter("T1"."DEP_ID"='kk')
   6 - access("T1"."ID"="T2"."ID")
   7 - filter("T2"."DEP_ID"='mm')

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)

35 rows selected.

SQL>
SQL>

可以看到第23,24行,在10g中运行时,buffers是0,而在12c中,即78,79行,buffer是2083+6496。

也就是说在10g中,外层查询不进行t1和t2的扫描,直接返回结果了,而在12c中,外层查询还要进行t1表和t2表层扫描才返回结果。

这其实不是10g和12c的差别,而是not exists的返回数据对外层的影响。子查询要返回0行记录,才满足not exist的条件,从而返回外层查询结果。

在10g中,子查询返回了一行记录

--10g
SQL> select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm';

         1
----------
         1

SQL>

不满足not exists(即0行才满足),所以,也就不用在外层继续查询了。直接返回记录0行。

在12c中,子查询返回0行记录,满足not exist的条件,所以还需要在外层查询中继续查询。

--12c
SQL> select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='mm';

no rows selected

SQL>

正是这一行记录的差异,导致了not exists对外层查询的影响。进而导致整个sql的buffer get的差异。

反证这个结果,我只要在12c中,运行子查询结果返回大于0行的,不满足not exists,也应该不会去外层查询了。见下:

--12c
SQL> select count(*) from t1,t2 where t1.id=t2.id and t2.dep_id='kk';
 
  COUNT(*)
----------
   1000000
 
SQL> set line 1000
SQL> set pages 1000
SQL> col PLAN_TABLE_OUTPUT for a250
SQL>
SQL>
SQL> select /*+ gather_plan_statistics */ count(*) from t1,t2 where t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2 where t1.id=t2.id and t2.dep_id='kk');
 
  COUNT(*)
----------
         0
 
SQL> select* from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));
 
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  c5hj2p2jt1fxf, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t1,t2 where
t1.id=t2.id and t1.dep_id='kk' and not exists (select 1 from t1,t2
where t1.id=t2.id and t2.dep_id='kk')
 
Plan hash value: 1692274438
 
--------------------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |      |      1 |        |      1 |00:00:00.28 |    2087 |       |    |     |
|   1 |  SORT AGGREGATE        |      |      1 |      1 |      1 |00:00:00.28 |    2087 |       |    |     |
|*  2 |   FILTER               |      |      1 |        |      0 |00:00:00.28 |    2087 |       |    |     |
|*  3 |    HASH JOIN           |      |      0 |   1215K|      0 |00:00:00.01 |       0 |    69M|  7428K|          |
|   4 |     TABLE ACCESS FULL  | T2   |      0 |   1215K|      0 |00:00:00.01 |       0 |       |    |     |
|*  5 |     TABLE ACCESS FULL  | T1   |      0 |   2738K|      0 |00:00:00.01 |       0 |       |    |     |
|*  6 |    HASH JOIN RIGHT SEMI|      |      1 |   2738K|      1 |00:00:00.28 |    2087 |    43M|  6111K|   42M (0)|
|*  7 |     TABLE ACCESS FULL  | T2   |      1 |   1215K|   1000K|00:00:00.12 |    2083 |       |    |     |
|   8 |     TABLE ACCESS FULL  | T1   |      1 |   2738K|      1 |00:00:00.01 |       4 |       |    |     |
--------------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   2 - filter( IS NULL)
   3 - access("T1"."ID"="T2"."ID")
   5 - filter("T1"."DEP_ID"='kk')
   6 - access("T1"."ID"="T2"."ID")
   7 - filter("T2"."DEP_ID"='kk')
 
Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
 
 
35 rows selected.
 
SQL>

可以看到第38,39行的buffer为0.

时间: 2024-11-05 20:28:45

oracle中not exists对外层查询的影响的相关文章

oracle中的exists 和not exists 用法详解(转)

有两个简单例子,以说明 "exists"和"in"的效率问题 1) select * from T1 where exists(select 1 from T2 where T1.a=T2.a) ;     T1数据量小而T2数据量非常大时,T1<<T2 时,1) 的查询效率高. 2) select * from T1 where T1.a in (select T2.a from T2) ;      T1数据量非常大而T2数据量小时,T1>&g

Oracle中的Exists、In、ANY、ALL

Exists:子查询至少返回一行时条件为true. Not Exists:子查询不返回任何一行时条件为true. In:与子查询返回结果集中某个值相等. Not In:与子查询返回结果集中任何一个值不相等. >ANY:比子查询返回结果中的某个值大. =ANY:与子查询返回结果中的某个值相等. <ANY:比子查询返回结果中的某个值小. >ALL:比子查询返回结果中的所有值都大. <ALL :比子查询返回结果中的所有值都小. 1.查询与10号部门某个员工工资相等的员工信息. selec

sqlserver和oracle中对datetime进行条件查询的一点区别小结_数据库其它

首先,看一下sql server,之前我们都通过前台用户选择一个起始时间和一个结束时间(以日为最小单位),然后来作为条件进行查询,如果直接通过"between starttime and endtime"来作为条件的话,发现会自动将"2009-06-17"转化为" 2009-06-17 00:00:00",于是如下查询条件" between '2009-06-16' and '2009-06-17'",只能得到16日的数据,1

Oracle中利用数据泵导出查询结果(二) 外部表的卸载功能

还是上一篇中的测试环境: SQL> CREATE TABLE T1 2  (ID NUMBER, NAME VARCHAR2(30)); Table created. SQL> INSERT INTO T1 2  SELECT ROWNUM, TNAME 3  FROM TAB; 66 rows created. SQL> CREATE TABLE T2 2  (ID NUMBER, NAME VARCHAR2(30)); Table created. SQL> INSERT IN

Oracle中利用数据泵导出查询结果(一) 数据泵的QUERY功能

在ITPUB上看到有人提出这个问题,能否利用数据泵导出一个查询结果.事实上数据泵还真的具有这个功能. 建立一个简单的测试环境: SQL> CREATE TABLE T1 2  (ID NUMBER, NAME VARCHAR2(30)); Table created. SQL> INSERT INTO T1 2  SELECT ROWNUM, TNAME 3  FROM TAB; 66 rows created. SQL> CREATE TABLE T2 2  (ID NUMBER, N

oracle中connect by prior语句查询父亲节点的问题

问题描述 已以下的sql文我以某个节点,向上查询它的所有父亲节点:select * from temp start with id = '10' connect by prior parent_id = id可是,现在我以下面的sql文,想取得所有父亲节点的id,却只能获得'10'这条记录:select id from temp start with id = '10' connect by prior parent_id = id问:1.为什么把"select *" 换成"

Oracle中的联合主键查询问题

最近要进行导数的工作,从好几张表中导入到一张表,其中数据可能重复,所以在导入之前要进行数据的比对. 方法一:我用group by having count(*)>1将重复的数据提取出来,然后进行人工比对,事实证明,有够笨的! 方法二:请教了一位资深人员,提供了一个更好的方法.就是将三个字段连接起来作为主键,进行数据是否重复的判断.这里值得注意的是某列的值可能为空,所以要赋一个空字符串过去. select * from t1 where nvl(col1,'')|| nvl(col2,'')| |

在Mysql,SqlServer及Oracle中设置主键自动增长

1.把主键定义为自动增长标识符类型 在mysql中,如果把表的主键设为auto_increment类型,数据库就会自动为主键赋值.例如: create table customers(id int auto_increment primary key not null, name varchar(15)); insert into customers(name) values("name1"),("name2"); select id from customers;

ORACLE中查询第n条到第m条的数据记录的方法

一.经过测试,下面的方法通过:   SELECT * FROM             (                  SELECT 表名.*, ROWNUM AS CON FROM 表名 WHERE ROWNUM <= M AND 其它查询条件 ORDER BY 排序条件              )WHERE CON >=N;   二.参考其它网上的方法   SQL/Oracle取出第 m 条到第 n 条记录的方法   用一句SQL取出第 m 条到第 n 条记录的方法 用一句SQL取