热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

子查询中的filter剖析

在oracle8i时候,子查询展开为表连接有很多的限制,所以经常我们会听见in和exist的写法会影响sql的性能,会看见很多文章提到一个论点是:in操作是优先对子查询做操作,然后驱动外部的查询,exists则是优先进行外部的查询,然后去驱动子查询,这个结论真的

在oracle 8i时候,子查询展开为表连接有很多的限制,所以经常我们会听见in和exist的写法会影响sql的性能,会看见很多文章提到一个论点是:in操作是优先对子查询做操作,然后驱动外部的查询,exists则是优先进行外部的查询,然后去驱动子查询, 这个结论真的

在oracle 8i时候,子查询展开为表连接有很多的限制,所以经常我们会听见in和exist的写法会影响sql的性能,会看见很多文章提到一个论点是:in操作是优先对子查询做操作,然后驱动外部的查询,exists则是优先进行外部的查询,然后去驱动子查询,

这个结论真的正确吗,我们来看看如果我们关闭子查询展开为表连接的特性,再来看看是否真的如上述所述:
SQL> create table xiaoyu03 as select * from dba_objects;
SQL> create table xiaoyu04 as select * from dba_objects where rownum<10;
SQL> explain plan for select /*+optimizer_features_enable('8.1.7')*/* from xiaoyu04 a where a.object_id in (select /*+no_unnest*/object_id from xiaoyu03 b where b.object_type='TABLE');

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2861815236

---------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
---------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16 | 3312 | 1 |
|* 1 | FILTER | | | | |
| 2 | TABLE ACCESS FULL| XIAOYU04 | 16 | 3312 | 1 |
|* 3 | TABLE ACCESS FULL| XIAOYU03 | 1 | 11 | 24 |
---------------------------------------------------------------

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

1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "XIAOYU03" "B"
WHERE "OBJECT_ID"=:B1 AND "B"."OBJECT_TYPE"='TABLE'))
3 - filter("OBJECT_ID"=:B1 AND "B"."OBJECT_TYPE"='TABLE')

Note
-----
- cpu costing is off (consider enabling it)

我们发现了虽然关闭了子查询为表连接,优化器也回到了8I的版本,这里只是表连接的方式改为了filter,然后依然是以外部的表作为驱动表,子查询的表作为被驱动表来做关联的,这个对于之前我们推断in一定先走子查询为驱动表是不合理的:
1 子查询展开为表连接下,此时是两个结果集的关联,优化器为选择少的结果集为驱动表,in的方式不能保证此时优化器一定先选择子查询为驱动表
2 子查询不展开表连接下,此时优化器会选择filter关联方式,优化器选择了主查询的表为驱动表,做filter关联子查询的结果集,in的方式依然不能保证此时优化器一定先走子表

可能有朋友对上面的论点2有异议:优化器这里做了一个查询转换,将in改写为了exists的方式,这个好像是为了满足优化器的驱动准则,就是用小的结果集去做驱动表,这里xiaoyu04比较少,所以这里选择用xiaoyu04去做驱动表是最佳的,而in的写法优化器得先走子表xiaoyu03,然后再走主表xiaoyu04,查询转换in为exists后,优化器可以先走xiaoyu04然后走xiaoyu03了,这个貌似看起来非常合理了,事实真是如下?

这里我们改变一下查询的关系,xiaoyu03是大表在主查询中,xiaoyu04是小表在子查询中,优化器回到11g的版本,而且此时我们还建立xiaoyu03的object_id的一个高效索引,再来看下no_unnest hint作用下的关联顺序:
SQL> create index ind_objid_xiaoyu03 on xiaoyu03(object_id);

Index created.
SQL> explain plan for select * from xiaoyu03 a where a.object_id in(select /*+no_unnest*/b.object_id from xiaoyu04 b where b.object_type='TABLE');

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2805971548

-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 88 | 14764 (1)| 00:02:58 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| XIAOYU03 | 9811 | 843K| 43 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| XIAOYU04 | 1 | 10 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------

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

1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "XIAOYU04" "B"
WHERE "B"."OBJECT_ID"=:B1 AND "B"."OBJECT_TYPE"='TABLE'))
3 - filter("B"."OBJECT_ID"=:B1 AND "B"."OBJECT_TYPE"='TABLE')

17 rows selected.
还是以主查询的表为驱动表做filter关联。

同样我们再来看看exists是否也是如此,关闭子查询为表连接后,优化器回到11g的版本,exists的sql语句能否将小结果集的子查询作为驱动表,而将大结果集的外部表作为被驱动表了:
SQL> explain plan for select * from xiaoyu03 a where exists(select /*+no_unnest*/1 from xiaoyu04 b where b.object_type='TABLE' and a.object_id=b.object_id);

Explained.

SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2805971548

-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 88 | 14764 (1)| 00:02:58 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| XIAOYU03 | 9811 | 843K| 43 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL| XIAOYU04 | 1 | 10 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------

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

1 - filter( EXISTS (SELECT /*+ NO_UNNEST */ 0 FROM "XIAOYU04" "B"
WHERE "B"."OBJECT_ID"=:B1 AND "B"."OBJECT_TYPE"='TABLE'))
3 - filter("B"."OBJECT_ID"=:B1 AND "B"."OBJECT_TYPE"='TABLE')

17 rows selected.

看来对于关闭子查询为表连接后,exists确实还是只能先扫描外部的表为驱动表,然后去驱动子查询的表。

这里再次要推翻我们之前的理解:就是优化器这里选择驱动表是考虑了选择小的驱动结果集为驱动表,会通过查询转换in和exists来满足小的结果集去做驱动表。

那么优化器为什么无论in还是exists的方式,在关闭子查询为表连接的特性后,都是选择的外部的表作为驱动表了,其实解释这个现象的根本原因是关联方式filter。

filter究竟如何工作:
表关联方式filter跟nested loop是相同的,只是filter维护了一个hash table用于记录了满足主表和子表关联的关联列和这个关联列相对应的这行数据的部分列。

确实很不好解释这个filter,我们举个简单的例子吧:比如查询是select * from xiaoyu03 a where exists(select /*+no_unnest*/1 from xiaoyu04 b where b.object_type='TABLE' and a.object_id=b.object_id);

oracle这里如果走fiter的关联方式,先从xiaoyu03表取出一条数据,然后a.object_id=b.object_id和b.object_type='TABLE'关联,如果满足,则记录a.object_id的值到hash table,返回满足这个关联条件的a.object_id对应的数据列;然后重复的取出所有xiaoyu03表的数据,如果发现下一个a.object_id已经记录到了hash table中则优化器不再需要去和xiaoyu04表做关联,而是直接再次返回a.object_id对应的这行数据列,这种filter关联方式只能选择主表做驱动表,所以我们看见了在关闭子查询转换为表连接后in和exists对应的filter执行计划都是以外部的表去做驱动表。

所以一般而言filter相比nested loop高效一点,对于重复的满足的关联列节省被驱动表的IO消耗。

推荐阅读
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • ubuntu用sqoop将数据从hive导入mysql时,命令: ... [详细]
  • Postgresql备份和恢复的方法及命令行操作步骤
    本文介绍了使用Postgresql进行备份和恢复的方法及命令行操作步骤。通过使用pg_dump命令进行备份,pg_restore命令进行恢复,并设置-h localhost选项,可以完成数据的备份和恢复操作。此外,本文还提供了参考链接以获取更多详细信息。 ... [详细]
  • REVERT权限切换的操作步骤和注意事项
    本文介绍了在SQL Server中进行REVERT权限切换的操作步骤和注意事项。首先登录到SQL Server,其中包括一个具有很小权限的普通用户和一个系统管理员角色中的成员。然后通过添加Windows登录到SQL Server,并将其添加到AdventureWorks数据库中的用户列表中。最后通过REVERT命令切换权限。在操作过程中需要注意的是,确保登录名和数据库名的正确性,并遵循安全措施,以防止权限泄露和数据损坏。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • 本文介绍了一些Java开发项目管理工具及其配置教程,包括团队协同工具worktil,版本管理工具GitLab,自动化构建工具Jenkins,项目管理工具Maven和Maven私服Nexus,以及Mybatis的安装和代码自动生成工具。提供了相关链接供读者参考。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了StartingzookeeperFAILEDTOSTART相关的知识,希望对你有一定的参考价值。下载路径:https://ar ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • SAP羞辱国产软件商:技术停在10年前
    SAP中国研究院总裁芮祥麟表示,国产软件厂商过于热衷概念炒作,技术水平停留在10年前的客户端架构水平。他认为,国内厂商推出基于SOA的产品或转型SAAS模式是不可能的,研发新架构需要时间。当前最热门的概念是云计算,芮祥麟呼吁国产厂商应该潜心研发底层架构。 ... [详细]
  • IT方面的论坛太多了,有综合,有专业,有行业,在各个论坛里混了几年,体会颇深,以前是论坛哪里人多 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • 本文讲述了孙悟空写给白骨精的信件引发的思考和反省。孙悟空在信中对自己的行为进行了反思,认识到自己胡闹的行为并没有给他带来实际的收获。他也揭示了西天取经的真相,认为这是玉皇、菩萨设下的一场陷阱。他还提到了师傅的虚伪和对自己的实心话,以及自己作为师傅准备提拔的对象而被派下来锻炼的经历。他认为路上的九九八十一难也都是菩萨算计好的,唐僧并没有真正的危险。最后,他提到了观音菩萨在关键时刻的指导。这封信件引发了孙悟空对自己行为的思考和反省,对西天取经的目的和自己的角色有了更深入的认识。 ... [详细]
  • Windows2003 IIS上设置301定向,实现不带www域名跳转带www域名的方法
    打开IIS,建一个网站,主机头用不带www的域名,随便指向一个目录。然后在这个网站上点右键,属性--主目录--重定向到URL如图ÿ ... [详细]
  • 本文介绍了在Ubuntu下制作deb安装包及离线安装包的方法,通过备份/var/cache/apt/archives文件夹中的安装包,并建立包列表及依赖信息文件,添加本地源,更新源列表,可以在没有网络的情况下更新系统。同时提供了命令示例和资源下载链接。 ... [详细]
  • 本文讨论了读书的目的以及学习算法的重要性,并介绍了两个算法:除法速算和约瑟夫环的数学算法。同时,通过具体的例子和推理,解释了为什么x=x+k序列中的第一个人的位置为k,以及序列2和序列3的关系。通过学习算法,可以提高思维能力和解决问题的能力。 ... [详细]
author-avatar
码农
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有