小知识:IN和EXISTS的用法及效率验证

知识,in,exists,用法,效率,验证 · 浏览次数 : 278

小编点评

生成内容时需要带简单的排版,例如: 1. **SQL ID**: 在每个SQL语句前面加上其ID,方便排版。 2. ** SQL PLAN HASH VALUE**: 在每个SQL语句前面加上其HASH VALUE,方便排版。 3. ** SQL PLAN LINE ID**: 在每个SQL语句前面加上其LINE ID,方便排版。 4. ** COUNT(*)**: 在每个SQL语句后面加上其COUNT(*),方便排版。 5. ** ORDER BY**: 在每个SQL语句后面加上其ORDER BY,方便排版。 6. ** WHERE clause**: 在每个WHERE clause后面加上其条件,方便排版。 7. ** GROUP BY**: 在每个GROUP BY clause后面加上其条件,方便排版。 8. ** ORDER BY**: 在每个ORDER BY clause后面加上其条件,方便排版。

正文

环境: Oracle 19.16 多租户架构
经常会在网上看到有人写exists和in的效率区别,其实在新版本的数据库中,是不存在这个问题的,优化器会自己判断选择最优的执行计划。

为了直观的说明,我在PDB中构造如下测试用例:

vi 1.sql

select count(*) from v$active_session_history;
select count(*) from dba_hist_active_sess_history;
create table T1 as select * from v$active_session_history;
create table T2 as select * from dba_hist_active_sess_history;

构造小表T1,大表T2。

SQL> set timing on
SQL> @1

  COUNT(*)
----------
       383

Elapsed: 00:00:00.05

  COUNT(*)
----------
    215636

Elapsed: 00:00:00.95

Table created.

Elapsed: 00:00:00.20

Table created.

Elapsed: 00:00:07.90

网上说,当T1数据量小,而T2数据量非常大时,使用exists的查询效率会高。
验证下,是否事实真是如此?

select /*+ monitor */ * from T1 where exists(select 1 from T2 where T1.sql_id = T2.sql_id) ;

select /*+ monitor */ * from T1 where T1.sql_id in (select T2.sql_id from T2) ;

SQL> select sql_id, sql_text from v$sql where sql_text like '%T2.sql_id%'

SQL_ID        SQL_TEXT
------------- ------------------------------------------------------------------------------------------
4xu586p9h0qcq select /*+ monitor */ * from T1 where T1.sql_id in (select T2.sql_id from T2)
3qgrm97t5jgwj select /*+ monitor */ * from T1 where exists(select 1 from T2 where T1.sql_id = T2.sql_id)

使用sqlmon取到两个SQL对应的SQL Monitor Report,对比分析发现:
二者执行计划完全一样,对应Plan Hash Value 1713220790,都走的是Hash Join Semi,执行时间也没差别。
所以这个说法最起码在Oracle 19c的版本中是不存在的,你想怎么写都OK,优化器会帮你做查询转换。

为了进一步验证,构造4个典型SQL,分别使用in和exists的写法:

--SQL1:
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
from T1
where T1.sql_id in (select T2.sql_id from T2)
group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
order by 1;

--SQL2:
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
from T1
where exists (select 1 from T2 where T2.sql_id = T1.sql_id)
group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
order by 1;

--SQL3:
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
from T2
where T2.sql_id in (select T1.sql_id from T1)
group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
order by 1;

--SQL4:
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
from T2
where exists (select 1 from T1 where T1.sql_id = T2.sql_id)
group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
order by 1;

SQL Monitor的截图就不贴了,直接给大家看下文本格式的执行计划,方便对比和检索:

SQL1:

SQL> select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
  2  from T1
  3  where T1.sql_id in (select T2.sql_id from T2)
  4  group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
  5  order by 1;

SQL_ID        SQL_PLAN_HASH_VALUE SQL_PLAN_LINE_ID   COUNT(*)
------------- ------------------- ---------------- ----------
3dbzmtf9ahvzt          3238164414                1          1
3kqrku32p6sfn          2977818336               14          1
3zbvwad7h1pgt          2360206614                1          2
3zbvwad7h1pgt          2360206614                           6
87gaftwrm2h68                   0                           1
9wncfacx0nj9h                   0                           2
9wncfacx0nj9h          3312548573                           9
avf5k3k0x0cxn          3746835944                1          1
b13g21mgg8y98           212733457                9          1
b13g21mgg8y98           212733457               12          2
ggh55rhz95kyj          3124993369                           8
gug127tbfzjcs          3645025857                0          1

12 rows selected.

Elapsed: 00:00:00.07
SQL> @xplan

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  250w6cua1mfa0, child number 2
-------------------------------------
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID,
count(*) from T1 where T1.sql_id in (select T2.sql_id from T2) group by
SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID order by 1

Plan hash value: 910330555

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |      1 |        |     12 |00:00:00.07 |   16132 |       |       |          |
|   1 |  SORT GROUP BY      |      |      1 |    228 |     12 |00:00:00.07 |   16132 |  2048 |  2048 | 2048  (0)|
|*  2 |   HASH JOIN SEMI    |      |      1 |    228 |     35 |00:00:00.07 |   16132 |  1376K|  1376K| 1604K (0)|
|*  3 |    TABLE ACCESS FULL| T1   |      1 |    228 |    228 |00:00:00.01 |      26 |       |       |          |
|*  4 |    TABLE ACCESS FULL| T2   |      1 |    177K|    177K|00:00:00.06 |   16106 |       |       |          |
-----------------------------------------------------------------------------------------------------------------

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

   2 - access("T1"."SQL_ID"="T2"."SQL_ID")
   3 - filter("T1"."SQL_ID" IS NOT NULL)
   4 - filter("T2"."SQL_ID" IS NOT NULL)


25 rows selected.

Elapsed: 00:00:00.04

SQL2:

SQL> select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
  2  from T1
  3  where exists (select 1 from T2 where T2.sql_id = T1.sql_id)
  4  group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
  5  order by 1;

SQL_ID        SQL_PLAN_HASH_VALUE SQL_PLAN_LINE_ID   COUNT(*)
------------- ------------------- ---------------- ----------
3dbzmtf9ahvzt          3238164414                1          1
3kqrku32p6sfn          2977818336               14          1
3zbvwad7h1pgt          2360206614                1          2
3zbvwad7h1pgt          2360206614                           6
87gaftwrm2h68                   0                           1
9wncfacx0nj9h                   0                           2
9wncfacx0nj9h          3312548573                           9
avf5k3k0x0cxn          3746835944                1          1
b13g21mgg8y98           212733457                9          1
b13g21mgg8y98           212733457               12          2
ggh55rhz95kyj          3124993369                           8
gug127tbfzjcs          3645025857                0          1

12 rows selected.

Elapsed: 00:00:00.06
SQL> @xplan

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  cxn8artthq7p8, child number 0
-------------------------------------
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID,
count(*) from T1 where exists (select 1 from T2 where T2.sql_id =
T1.sql_id) group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID order
by 1

Plan hash value: 910330555

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation           | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |      1 |        |     12 |00:00:00.06 |   16132 |       |       |          |
|   1 |  SORT GROUP BY      |      |      1 |    228 |     12 |00:00:00.06 |   16132 |  2048 |  2048 | 2048  (0)|
|*  2 |   HASH JOIN SEMI    |      |      1 |    228 |     35 |00:00:00.06 |   16132 |  1376K|  1376K| 1611K (0)|
|*  3 |    TABLE ACCESS FULL| T1   |      1 |    228 |    228 |00:00:00.01 |      26 |       |       |          |
|*  4 |    TABLE ACCESS FULL| T2   |      1 |    177K|    177K|00:00:00.05 |   16106 |       |       |          |
-----------------------------------------------------------------------------------------------------------------

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

   2 - access("T2"."SQL_ID"="T1"."SQL_ID")
   3 - filter("T1"."SQL_ID" IS NOT NULL)
   4 - filter("T2"."SQL_ID" IS NOT NULL)


26 rows selected.

Elapsed: 00:00:00.03

SQL3:

SQL> select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
  2  from T2
  3  where T2.sql_id in (select T1.sql_id from T1)
  4  group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
  5  order by 1;

SQL_ID        SQL_PLAN_HASH_VALUE SQL_PLAN_LINE_ID   COUNT(*)
------------- ------------------- ---------------- ----------
3dbzmtf9ahvzt          3238164414                1          1
3kqrku32p6sfn          1774581179               20          2
3kqrku32p6sfn          1774581179               23          2
3kqrku32p6sfn          2977818336               14          2
3zbvwad7h1pgt          2360206614                           1
87gaftwrm2h68          1072382624                2          2
9wncfacx0nj9h          3312548573                           2
avf5k3k0x0cxn          3746835944                1          1
b13g21mgg8y98           212733457                9          1
b13g21mgg8y98          2612542848                1          2
ggh55rhz95kyj          3124993369                           4
gug127tbfzjcs          3645025857                           1

12 rows selected.

Elapsed: 00:00:00.09
SQL> @xplan

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  1588n6cc48yv0, child number 0
-------------------------------------
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID,
count(*) from T2 where T2.sql_id in (select T1.sql_id from T1) group by
SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID order by 1

Plan hash value: 3152222881

-------------------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |      1 |        |     12 |00:00:00.08 |   16132 |       |       |          |
|   1 |  SORT GROUP BY        |      |      1 |   3684 |     12 |00:00:00.08 |   16132 |  2048 |  2048 | 2048  (0)|
|*  2 |   HASH JOIN RIGHT SEMI|      |      1 |   3684 |     21 |00:00:00.08 |   16132 |  1651K|  1651K| 1520K (0)|
|*  3 |    TABLE ACCESS FULL  | T1   |      1 |    228 |    228 |00:00:00.01 |      26 |       |       |          |
|*  4 |    TABLE ACCESS FULL  | T2   |      1 |    177K|    177K|00:00:00.08 |   16106 |       |       |          |
-------------------------------------------------------------------------------------------------------------------

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

   2 - access("T2"."SQL_ID"="T1"."SQL_ID")
   3 - filter("T1"."SQL_ID" IS NOT NULL)
   4 - filter("T2"."SQL_ID" IS NOT NULL)


25 rows selected.

Elapsed: 00:00:00.03

SQL4:

SQL> select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID, count(*)
  2  from T2
  3  where exists (select 1 from T1 where T1.sql_id = T2.sql_id)
  4  group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID
  5  order by 1;

SQL_ID        SQL_PLAN_HASH_VALUE SQL_PLAN_LINE_ID   COUNT(*)
------------- ------------------- ---------------- ----------
3dbzmtf9ahvzt          3238164414                1          1
3kqrku32p6sfn          1774581179               20          2
3kqrku32p6sfn          1774581179               23          2
3kqrku32p6sfn          2977818336               14          2
3zbvwad7h1pgt          2360206614                           1
87gaftwrm2h68          1072382624                2          2
9wncfacx0nj9h          3312548573                           2
avf5k3k0x0cxn          3746835944                1          1
b13g21mgg8y98           212733457                9          1
b13g21mgg8y98          2612542848                1          2
ggh55rhz95kyj          3124993369                           4
gug127tbfzjcs          3645025857                           1

12 rows selected.

Elapsed: 00:00:00.09
SQL> @xplan

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  99fkm9p94agcf, child number 0
-------------------------------------
select /*+ monitor */ SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID,
count(*) from T2 where exists (select 1 from T1 where T1.sql_id =
T2.sql_id) group by SQL_ID, SQL_PLAN_HASH_VALUE, SQL_PLAN_LINE_ID order
by 1

Plan hash value: 3152222881

-------------------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |      |      1 |        |     12 |00:00:00.09 |   16132 |       |       |          |
|   1 |  SORT GROUP BY        |      |      1 |   3684 |     12 |00:00:00.09 |   16132 |  2048 |  2048 | 2048  (0)|
|*  2 |   HASH JOIN RIGHT SEMI|      |      1 |   3684 |     21 |00:00:00.09 |   16132 |  1651K|  1651K| 1520K (0)|
|*  3 |    TABLE ACCESS FULL  | T1   |      1 |    228 |    228 |00:00:00.01 |      26 |       |       |          |
|*  4 |    TABLE ACCESS FULL  | T2   |      1 |    177K|    177K|00:00:00.08 |   16106 |       |       |          |
-------------------------------------------------------------------------------------------------------------------

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

   2 - access("T1"."SQL_ID"="T2"."SQL_ID")
   3 - filter("T1"."SQL_ID" IS NOT NULL)
   4 - filter("T2"."SQL_ID" IS NOT NULL)


26 rows selected.

Elapsed: 00:00:00.03

可以看到对比前2个SQL:
250w6cua1mfa0
cxn8artthq7p8
执行计划一样,都是HASH JOIN SEMI。

对比后两个SQL:
1588n6cc48yv0
99fkm9p94agcf
执行计划也一样,都是HASH JOIN RIGHT SEMI。

所以,在新版本的数据库中,确实是不用再关注这个问题,优化器会帮助我们做好最优的查询转换。

与小知识:IN和EXISTS的用法及效率验证相似的内容:

小知识:IN和EXISTS的用法及效率验证

环境: Oracle 19.16 多租户架构 经常会在网上看到有人写exists和in的效率区别,其实在新版本的数据库中,是不存在这个问题的,优化器会自己判断选择最优的执行计划。 为了直观的说明,我在PDB中构造如下测试用例: vi 1.sql select count(*) from v$acti

小知识:SQL Monitor Report的使用

在上一篇 优化利器In-Memory开启和效果 中,提到的两个SQL对比,使用的是传统的dbms_xplan.display_cursor方式来查看执行计划,好处是文本输出的通用性强,基本信息也都有。 但如果大家参加过我们的RWP培训,就会发现O原厂强烈推荐大家使用的一个工具是 SQL Monito

小知识:什么叫做workaround?

技术人当遇到具体问题,能给出的各种解决方案,有一种类型叫做workaround,翻译过来通常为“应变方法”、“变通方法”; 其实这种方式通常是没有找到根本的解决方案,但是为了快速恢复业务而采用的一种巧妙规避/跳过的方式。 举个具体的例子:我有测试需求要在主库创建一个新的PDB: 1.创建新的PDB

小知识:使用errorstack定位特定问题

有客户遇到ORA-2289的报错,同事协助去现场排查,我帮着远程共同check下。 客户只是应用端报出的错误,为了进一步定位,服务端需要开errorstack协助定位具体问题。 下面就以这个ORA-2289为例,示范下errorstack的使用方法。 --开启errorstack alter sys

小知识:使用oracle用户查看RAC集群资源状态

正常情况按照标准配置的环境变量,只能grid用户查看RAC集群资源状态。 crsctl stat res -t 但是绝大部分操作其实都是oracle用户来操作,比如启停数据库,操作完成以后就需要检查下集群资源状态。 看到好多DBA在现场操作时就是来回各种切换或开多个窗口。 其实有两个简单的解决方法可

小知识:设置archive_lag_target参数强制日志切换

为客户测试一个ADG场景问题,发现测试环境的日志切换频率过低,总是需要定期手工切换,这非常影响测试心情。 实际上,可以设置archive_lag_target参数强制日志切换。 比如设置: alter system set archive_lag_target=1800; 这样即使库没任何压力,半小

小知识:grep过滤以#号开头的注释行 和 空行

xtts的配置文件,有很多注释不想直接去掉的情况下,想清楚的看到目前设置了哪些参数,可以用grep过滤查看: `grep -vE '^#|^$' xtt.properties` 效果如下: ```shell [oracle@db11gcas xtt]$ grep -vE '^#|^$' xtt.pr

小知识:开启NTP服务并设置为开机启动

我的一套测试环境发现时间慢了10分钟,影响我做各类测试。 首先就想到NTP服务,发现已安装NTP安装包,也有默认的NTP配置文件,只是没有启用。 用到的相关命令参考如下: ```shell [root@bogon ~]# vi /etc/ntp.conf systemctl status ntpd

小知识:PPT的幻灯片放映设置

最近给某客户讲课时,碰到了幻灯片自动翻页的情况,发现是因为之前做过粗略的计时演练,有些片子就快速过了。 **问题现象:** 结果导致放映时也出现了某些片子快速被自动翻页。 **解决方案:** 设置成手动推进幻灯片的方式即可解决,具体如下图所示: ![](https://img2023.cnblogs

[转帖]【网络小知识】之TCP IP 五元组(five-tuple/5-tuple)

为什么要分享TCP IP 5元组(five-tuple/5-tuple的知识? 最近在进行深度分析过程中,听到某些资深人士提到了5元组这个概念,觉得很高大尚,去搜索了一圈,发现都是些非常浅显的知识,对于tcp ip 5元组,7元组有什么用没有提及,也没有五元组的英文,导致英文资料检索过程中饶了一圈。