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

子查询中的filter剖析-mysql教程

在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消耗。

推荐阅读
  • 智能制造数据综合分析与应用解决方案
    在智能制造领域,生产数据通过先进的采集设备收集,并利用时序数据库或关系型数据库进行高效存储。这些数据经过处理后,通过可视化数据大屏呈现,为生产车间、生产控制中心以及管理层提供实时、精准的信息支持,助力不同应用场景下的决策优化和效率提升。 ... [详细]
  • 在Hive中合理配置Map和Reduce任务的数量对于优化不同场景下的性能至关重要。本文探讨了如何控制Hive任务中的Map数量,分析了当输入数据超过128MB时是否会自动拆分,以及Map数量是否越多越好的问题。通过实际案例和实验数据,本文提供了具体的配置建议,帮助用户在不同场景下实现最佳性能。 ... [详细]
  • 在处理历史交易表时,发现存在部分重复交易记录,需进行数据清理。为解决此问题,考虑构建一个临时表,并采用SQL Server ODBC工具进行数据的导入与导出操作,以实现高效去重。此外,建议结合索引优化和批处理技术,进一步提升数据处理效率和系统性能。 ... [详细]
  • 优化Oracle数据库日志功能的关闭方法与实践
    在优化Oracle数据库日志功能的过程中,关闭不必要的日志记录是一项重要任务。本文探讨了Oracle 11g中日志路径的配置和管理,特别是针对常用的警报日志(alert log)。通过合理配置 `alert_$ORACLE_SID.log` 文件,可以有效减少日志文件的大小和提高系统性能。此外,文章还介绍了如何通过调整参数和使用脚本自动化日志管理,进一步提升数据库的稳定性和维护效率。 ... [详细]
  • 多种实现 Windows 定时自动执行任务的专业技巧与方案
    在Windows系统中,实现定时自动执行任务有多种专业技巧和方案。常见的方法包括:使用Windows任务计划程序、开发Windows服务以及利用SQL Server Agent作业。这些方法被广泛应用于各种自动化场景,多数技术人员对此都有所了解。 ... [详细]
  • 如何在Oracle ASM_Diskgroup中重命名现有磁盘
    如何在Oracle ASM_Diskgroup中重命名现有磁盘 ... [详细]
  • ESP32 IRAM 内存优化策略与实践总结
    本文总结了针对ESP32 IRAM内存溢出问题的优化策略与实践经验。通过详细分析ESP32的内存结构和IRAM分配机制,提出了一系列有效的解决方案,包括代码优化、内存管理技巧和编译器配置调整,旨在帮助开发者有效解决`.espressif/tools/xtensa-esp32-elf/esp-2`等类似错误,提升系统性能和稳定性。 ... [详细]
  • 在Linux系统中,原本已安装了多个版本的Python 2,并且还安装了Anaconda,其中包含了Python 3。本文详细介绍了如何通过配置环境变量,使系统默认使用指定版本的Python,以便在不同版本之间轻松切换。此外,文章还提供了具体的实践步骤和注意事项,帮助用户高效地管理和使用不同版本的Python环境。 ... [详细]
  • 在从深圳返回武汉的旅途中,我购买并阅读了《史玉柱亲述营销智慧》一书。我对史玉柱的兴趣源于对其脑白金广告策略演变的观察,最初是出于对其营销手法的质疑和批评。然而,随着对书中内容的深入理解,逐渐认识到他独特的营销理念和策略背后的深刻洞察力。这本书不仅详细剖析了史玉柱的商业成功之路,还提供了许多实用的营销技巧和经验分享,对于希望提升自身营销能力的读者来说,具有极高的参考价值。 ... [详细]
  • 本文深入探讨了IO复用技术的原理与实现,重点分析了其在解决C10K问题中的关键作用。IO复用技术允许单个进程同时管理多个IO对象,如文件、套接字和管道等,通过系统调用如`select`、`poll`和`epoll`,高效地处理大量并发连接。文章详细介绍了这些技术的工作机制,并结合实际案例,展示了它们在高并发场景下的应用效果。 ... [详细]
  • 作为一名技术娴熟的编程专家,我通过多年的经验积累和对代码片段的系统总结,几乎无需额外查阅资料。相比之下,普通开发人员在项目开发中通常需要花费10%-20%的时间用于资料查询。我特别注重代码的复用性和高效性,这不仅提高了开发效率,还保证了代码的高质量和可维护性。 ... [详细]
  • 本文探讨了在 Silverlight 应用程序中实现多窗口界面的方法,通过引入浮动窗口组件,增强了应用的交互性和用户体验。该方案详细介绍了技术实现过程,包括窗口管理、布局调整及跨窗口通信机制,为开发者提供了实用的参考。 ... [详细]
  • 如何在PHP中实现链接输出与字符串连接的操作技巧 ... [详细]
  • 本文介绍了一种专为清洁工人设计的自定义文本烟花效果。通过该功能,用户可以输入特定的感谢或祝福语句,系统将生成绚丽的烟花动画,以表达对清洁工人的敬意和感激之情。该特效不仅美观,还能增强用户的互动体验,提升公共场合的氛围。 ... [详细]
  • 在Python 3环境中,当无法连接互联网时,可以通过下载离线模块包来实现模块的安装。具体步骤包括:首先从PyPI网站下载所需的模块包,然后将其传输到目标环境,并使用`pip install`命令进行本地安装。此方法不仅适用于单个模块,还支持依赖项的批量安装,确保开发环境的完整性和一致性。 ... [详细]
author-avatar
qlb
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有