Oracle 查询转换之子查询展开
概念:子查询展开(Subquery Unnesting)是优化器处理带子查询的目标sql的一种优化手段,它是指优化器不再将目标sql中子查询当作一个独立的处理单元来单独执行,而是将该子查询转换为它自身和外部查询之间等价的表连接。这种等价连接转换要么是将子查询展开(即将该子查询中的表,视图从子查询中拿出来,然后和外部查询中的表,视图做表连接),要么是不拆开但是会把该子查询转换为一个内嵌视图(Inline View)然后再和外部查询中的表,视图做表连接。Oracle 会确保子查询展开所对应的等价连接转换的正确性,即转换后的sql和原sql在语义上一定是等价的。当然不是所有的子查询都能做子查询展开,有些子查询是不能做这种等价表连接转换的,这种情况下oracle就不会对其做子查询展开,也就是说此时oracle还是会将该子查询当作一个独立的处理单元来单独执行。另外,在oracle10g以后版本中,对于那种不拆开子查询但是会把该子查询转换成一个内嵌视图的子查询展开,只有当经过子查询展开后的等价改写sql的成本值小于原sql的成本值时,oracle才会对原sql执行子查询展开
子查询展开通常都会提高原sql的执行效率,因为如果原sql不做子查询展开,那么通常情况下该子查询就会在其执行计划的最后一步才被执行,并且会走filter类型的执行计划,这也就意味着对于外部查询所在结果集的没一条记录,该子查询就会被执行多少次,这种执行方式的执行效率通常情况不会太高,尤其在子查询中包含两个或两个以上表连接时,此时做子查询展开后的执行效率往往会比走filter类型的执行计划高很多。
Oracle 数据库里子查询前where条件如果是如下这些条件之一,那么这种类型的目标sql在满足了一定条件后就可以做子查询展开,
single-row,exists,not exists,in ,not in,any,all。
范例1:
SQL> set lines 200 pagesize 1000
in写法:
SELECT t1.cust_last_name, t1.cust_idFROM customers t1WHERE t1.cust_id IN4 (SELECT t2.cust_id FROM sales t2 WHERE t2.amount_sold > 700)5 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2448612695
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7059 | 158K| | 1583 (1)| 00:00:20 | | |
|* 1 | HASH JOIN SEMI | | 7059 | 158K| 1360K| 1583 (1)| 00:00:20 | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 704K| | 405 (1)| 00:00:05 | | |
| 3 | PARTITION RANGE ALL| | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
|* 4 | TABLE ACCESS FULL | SALES | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
any等价写法:
SELECT t1.cust_last_name, t1.cust_idFROM customers t1WHERE t1.cust_id = ANY4 (SELECT t2.cust_id FROM sales t2 WHERE t2.amount_sold > 700)5 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2448612695
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7059 | 158K| | 1583 (1)| 00:00:20 | | |
|* 1 | HASH JOIN SEMI | | 7059 | 158K| 1360K| 1583 (1)| 00:00:20 | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 704K| | 405 (1)| 00:00:05 | | |
| 3 | PARTITION RANGE ALL| | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
|* 4 | TABLE ACCESS FULL | SALES | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
----------------------------------------------------------------------------------------------------------
exists等价写法:
SELECT t1.cust_last_name, t1.cust_idFROM customers t1WHERE t1.cust_id = ANY4 (SELECT t2.cust_id FROM sales t2 WHERE t2.amount_sold > 700)5 ;
Execution Plan
----------------------------------------------------------
Plan hash value: 2448612695
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7059 | 158K| | 1583 (1)| 00:00:20 | | |
|* 1 | HASH JOIN SEMI | | 7059 | 158K| 1360K| 1583 (1)| 00:00:20 | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 704K| | 405 (1)| 00:00:05 | | |
| 3 | PARTITION RANGE ALL| | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
|* 4 | TABLE ACCESS FULL | SALES | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
----------------------------------------------------------------------------------------------------------
不展开,显然不合理,sales表要执行很多次:
SELECT t1.cust_last_name, t1.cust_idFROM customers t1WHERE t1.cust_id IN (SELECT /*+ no_unnest */t2.cust_idFROM sales t2WHERE t2.amount_sold > 700)
子查询展开后,变成hash 半连接:
等价写法:(如果cust_id是唯一键值)可以转换为内连接:
SELECT t1.cust_last_name, t1.cust_idFROM customers t1, sales t2WHERE t1.cust_id= t2.cust_idAND t2.amount_sold > 700
如果是not in,则会转换为hash 反连接:
SQL> set autot trace
SELECT t1.cust_last_name, t1.cust_idFROM customers t1WHERE t1.cust_id not in 4 (SELECT t2.cust_id FROM sales t2 WHERE t2.amount_sold > 700);
Execution Plan
----------------------------------------------------------
Plan hash value: 2850422635
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 48441 | 1088K| | 1583 (1)| 00:00:20 | | |
|* 1 | HASH JOIN ANTI | | 48441 | 1088K| 1360K| 1583 (1)| 00:00:20 | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 704K| | 405 (1)| 00:00:05 | | |
| 3 | PARTITION RANGE ALL| | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
|* 4 | TABLE ACCESS FULL | SALES | 560K| 5469K| | 526 (2)| 00:00:07 | 1 | 28 |
----------------------------------------------------------------------------------------------------------
把子查询转换成内联视图:
SELECT t1.cust_last_name, t1.cust_idFROM customers t1WHERE t1.cust_id NOT IN(SELECT t2.cust_idFROM sales t2, products t3WHERE t2.prod_id = t3.prod_id and t2.amount_sold > 700)
Execution Plan
----------------------------------------------------------
Plan hash value: 1272298339
--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 48441 |1229K| |1665 (1)| 00:00:20 | | |
|* 1 | HASH JOIN ANTI | | 48441 |1229K|1360K|1665 (1)| 00:00:20 | | |
| 2 | TABLE ACCESS FULL | CUSTOMERS | 55500 | 704K| | 405 (1)| 00:00:05 | | |
| 3 | VIEW | VW_NSO_1 | 560K|7110K| | 529 (2)| 00:00:07 | | |
|* 4 | HASH JOIN | | 560K|9844K| | 529 (2)| 00:00:07 | | |
| 5 | INDEX FULL SCAN | PRODUCTS_PK | 72 | 288 | | 1 (0)| 00:00:01 | | |
| 6 | PARTITION RANGE ALL| | 560K|7657K| | 526 (2)| 00:00:07 | 1 | 28 |
|* 7 | TABLE ACCESS FULL | SALES | 560K|7657K| | 526 (2)| 00:00:07 | 1 | 28 |
--------------------------------------------------------------------------------------------------------------
这里oracle把子查询转换成内联视图 VM_NSO_1,然后再和外部查询中的表customers做hash半连接。
等价:
SELECT t1.cust_last_name, t1.cust_idFROM customers t1,(SELECT t2.cust_idFROM sales t2, products t3WHERE t2.prod_id = t3.prod_idAND t2.amount_sold > 700) vm_nso_1WHERE t1.cust_id semi = vm_nso_1.cust_id
子查询是否能够做子查询展开取决于如下两个条件:
子查询展开所对应的等价改写sql和原sql在语义上一定要完全等价的,如果改写后的sql和原sql并不一定能保持语义上的完全等价,这种类型的子查询就不能做子查询展开。
对于不能拆开的子查询但是会把它转换为一个内嵌视图的子查询展开,只有经过子查询展开的等价改写sql成本值小于原sql的成本值。oracle才会对目标sql执行子查询展开。
对于子查询展开的第一种情形(即将子查询展开,把该子查询中的表,视图从子查询中拿出来,然后和外部查询中表,视图做表连接),即使在oracle 10g以后的版本中,oracle也不会考虑子查询展开的成本,即oracle此时会认为这种情形下子查询展开的效率始终比不展开的效率高,这就意味着如果目标sql满足子查询展开的第一种情形。则oracle始终会做子查询展开,而不管经过子查询展开后的等价sql的成本值是否小于原sql的成本值。
转载于:https://blog.51cto.com/7642644/1700364
相关文章:

Xcode中通过删除原先版本的程序来复位App
可以在Xcode菜单中点击 Product->Clean Build Folder (按住Option键,在windows键盘中是Alt键.) 此时Xcode将会从设备中删除(卸载uninstall)任何该app之前部署的版本. 接下来重启Xcode,再试一下,有时这可以修复非常奇怪(really weird)的问题.

深入理解PHP之OpCode
OpCode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,或者.NET的MSL。 此文主要基于《 Understanding OPcode》和 网络,根据个人的理解和修改,特记录下来 :PHP代码: <?phpecho "Hello World";$a 1…

关于 AIOps 的过去与未来,微软亚洲研究院给我们讲了这些故事
作者 | 贾凯强出品 | AI科技大本营(ID:rgznai100)在过去的15年里,云计算实现了飞速发展,而这种发展也为诸多的前沿技术奠定了基础,AIOps便在此环境中获得了良好的发展契机。在数字化转型的浪潮下,云计算已经…

JS 正则表达式 0.001 ~99.999
^(0|[1-9][0-9]?)(\.[0-9]{0,2}[1-9])?$转载于:https://www.cnblogs.com/wahaha603/p/9050130.html

深入浅出PHP(Exploring PHP)
一直以来,横观国内的PHP现状,很少有专门介绍PHP内部机制的书。呵呵,我会随时记录下研究的心得,有机会的时候,汇总成书。:) 今天这篇,我内心是想打算做为一个导论: PHP是一个被广泛应用的脚本语言…

懒人神器 !一个创意十足的 Python 命令行工具
作者 | 写代码的明哥来源 | Python编程时光当听到某些人说 xx 库非常好用的时候,我们总是忍不住想要去亲自试试。有一些库,之所以好用,是对一些库做了更高级的封闭,你装了这个库,就会附带装了 n 多依赖库,就…

Regular Expression Matching
正则匹配 Regular Expression Matching Implement regular expression matching with support for . and *. . Matches any single character. * Matches zero or more of the preceding element.The matching should cover the entire input string (not partial).The functio…
PI校正环节的程序实现推导过程
PI校正环节在经典控制论中非常有用,特别是对负反馈控制系统,基本上都有PI校正环节。1.下面分别说明比例环节和积分环节的作用,以阶跃信号为例。①比例环节单独作用以上分析说明,若只有比例环节的控制系统,阶跃响应也是…

几行 Python 代码实现邮件解析,超赞~
作者 | Yunlor来源 | CSDN博客前言如何通过python实现邮件解析?邮件的格式十分复杂,主要是mime协议,本文主要是从实现出发,具体原理可以自行研究。一、安装通过mailgun开源的Flanker库实现邮件解析。该库包含了邮件地址解析和邮件…

深入理解PHP原理之变量(Variables inside PHP)
或许你知道,或许你不知道,PHP是一个弱类型,动态的脚本语言。所谓弱类型,就是说PHP并不严格验证变量类型(严格来讲,PHP是一个中强类型语言,这部分内容会在以后的文章中叙述),在申明一个变量的时候࿰…

jQuery中的.height()、.innerHeight()和.outerHeight()
jQuery中的.height()、.innerHeight()和.outerHeight()和W3C的盒模型相关的几个获取元素尺寸的方法。对应的宽度获取方法分别为.width()、.innerWidth()和.outerWidth(),在此不详述。1. .height()获取匹配元素集合中的第一个元素的当前计算高度值 或 设置每一个匹配…

Python实战之logging模块使用详解
用Python写代码的时候,在想看的地方写个print xx 就能在控制台上显示打印信息,这样子就能知道它是什么了,但是当我需要看大量的地方或者在一个文件中查看的时候,这时候print就不大方便了,所以Python引入了logging模块来…

深入理解PHP原理之变量作用域
作者:laruence(http://www.laruence.com/)地址: http://www.laruence.com/2008/08/26/463.html PHP变量的内部表示是如何和用户脚本中的变量联系起来的呢?也就是说,如果我在脚本中写下:<?php $var"laruen…

Azure AI的又一里程碑,Neural TTS新模型呈现真人般情感饱满的AI语音
在人与人之间的对话中,即使是同样的字句,也会因为所处情景和情感的不同而表现出丰富的抑扬顿挫,而这种动态性恰恰是各种AI合成语音的“软肋”。相比于人类讲话时丰富多变的语气,AI语音的“心平气和”往往给人带来明显的违和感。 …

VS2010中“工具选项中的VC++目录编辑功能已被否决”解决方法
http://blog.csdn.net/chaijunkun/article/details/6658923 这是VS2010的改变,不能够在“工具-选项”中看到“VC目录”了。 但是呢,我们可以在另外一个地方找到它,请看下边的对比照片。 VS2008中: VS2010中: 打开方式非…

Bminer 7.0.0 ETH挖矿教程(Linux 64)
Bminer产品介绍Bminer是目前最快的挖矿程序,Bminer是基于NVIDIA GPU深度优化的挖矿软件。Bminer支持Equihash和Ethash两种算法的虚拟币,包括:ETH(以太坊),ETC,ZEC(零币),…

深入理解PHP原理之变量分离/引用(Variables Separation)
引自: http://www.laruence.com/ [风雪之隅 ]在前面的文章中我已经介绍了PHP的变量的内部表示(深入理解PHP原理之变量(Variables inside PHP)),以及PHP中作用域的实现机制(深入理解PHP原理之变量作用域(Scope inside PHP))。这节我们就接着前面的文章,继…

C# 属性、索引
属性(property): public string Name {get{return _name;}set{_name value;} } 简写为: public string Name { set; get;} 索引器(index): 索引器为C#程序语言中泪的一种成员,它是的对象可…

分享几段祖传的 Python 代码,拿来直接使用!
作者 | 周萝卜来源 | 萝卜大杂烩今天分享几段工作生活中常用的代码,都是最为基础的功能和操作,而且大多还都是出现频率比较高的,很多都是可以拿来直接使用或者简单修改就可以放到自己的项目当中日期生成很多时候我们需要批量生成日期…
JVM——Java虚拟机架构
Java虚拟机(Java virtualmachine)实现了Java语言最重要的特征:即平台无关性。 平台无关性原理:编译后的 Java程序(.class文件)由 JVM执行。JVM屏蔽了与具体平台相关的信息,使程序可以在多种平台…

深入理解PHP之数组遍历
本文地址: http://www.laruence.com/2009/08/23/1065.html 经常会有人问我, PHP的数组, 如果用foreach来访问, 遍历的顺序是固定的么? 以什么顺序遍历呢? 比如: <?php$arr[laruence] huixinchen;$arr[yahoo] 2007;$arr[baidu] 2008;foreach ($arr as $key >…

Github 年度最受欢迎的 TOP30 Python 项目,超值
作者 | 俊欣来源 | 关于数据分析与可视化今天小编整理归纳了2021年Github上面最受欢迎的30个Python项目,帮助大家在打磨技术与提升自我上面更进一步。通过代码来获取Github官网有开源的接口,因此数据的获取也就方便了许多,代码如下url https…
Linux字符设备驱动程序的框架(新写法)
这是老版本内核的的Linux驱动注册函数写法: major register_chrdev(0, "hello", &hello_fops); /* (major, 0), (major, 1), ..., (major, 255)都对应hello_fops */ 新版本内核Linux驱动注册函数写法#define MAJOR(devid) ((unsigned int) ((devid…

将一个普通的java项目转化为maven项目
在学习Spring事务时,我参考的书的源码不是maven项目,整本书依赖的100多个jar包都在一个文件夹里,我本来对spring每个模块的学习源码都放在一个Github仓库里,每一个项目都是maven项目,这样想要将项目转化为maven项目&am…
深入理解PHP内存管理之谁动了我的内存
本文地址: http://www.laruence.com/2011/03/04/1894.html转载请注明出处首先让我们看一个问题: 如下代码的输出, var_dump(memory_get_usage());$a "laruence";var_dump(memory_get_usage());unset($a);var_dump(memory_get_usage()); 输出(在我的个人电脑上, 可能…

蓝懿教育九月二十七日记录
将VIew移动做成动画效果 这种动画效果没有中间的位移可以添加动画的View属性center,frame,alpha,transform , backgroundColor//继续做消失的动画[UIView animateWithDuration:1 animations:^{iv.alpha 0;} completion:^(BOOL finished) …

新年快到了,让我们一起用 Python 编织中国结吧
作者 | FrigidWinter来源 | CSDN博客新年快到了,今天博主教大家用Python编织中国结~中国结的组成部分中国结是一种手工编织工艺品,它身上所显示的情致与智慧正是汉族古老文明中的一个侧面。因为其外观对称精致,可以代表汉族悠久的历史&#x…

pwa+webpack,初探与踩坑
0.前言 我们都知道pwa是一个新技术.,依靠缓存,离线了还能正常跑,而且秒开。我把以前原生写的小游戏迁移到react,再迁移到webpackreact,最后再升级到pwa。具体介绍不多说,我们开始撸吧。 1.webpack webpack攻…

linux sar 命令详解
sar(System Activity Reporter系统活动情况报告)是目前 Linux 上最为全面的系统性能分析工具之一,可以从多方面对系统的活动进行报告,包括:文件的读写情况、系统调用的使用情况、磁盘I/O、CPU效率、内存使用状况、进程…

PHP底层工作原理
简介 先看看下面这个过程: 我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的;PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程序编程接口);PHP总共有三个模…