SlideShare a Scribd company logo
Something about Oracle Joins 丁俊  dingjun123 mailto:dingjunlove@163.com
内容简介 Oracle Join 简介 Join 重要性 Join 的类别 Examples 新旧 Join 语法 理解 SQL86 中的“ +” 理解 SQL92 的 on 和 where 何时用新语法 Examples
内容简介续 常见 Join 问题和 Tuning 关联 update 及优化 Semi Join 和 Anti Join 注意点 Set 操作转换
Oracle Join 简介 -Join 的重要性 Join 是关系型数据库的核心内容 Query 中经常使用表连接查询 DML 、 DDL 也经常使用到表连接 Oracle Join 有高效的算法作为性能支撑 Join 无处不在
Oracle Join 简介 -Join 的类别 Join 类别 Inner Join and Outer Join Equi-Join and Non-Equi-Join Semi Join and Anti Join Self join 、 Cross Join… 能够识别 Join 和使用对应的 Join 很重要
Oracle Join 简介 -Examples Non-equi-Join and self Join Example SELECT a.ename,b.ename, a.hiredate,b.hiredate FROM scott.emp a,scott.emp b WHERE a.hiredate <= b.hiredate AND  a.empno <> b.empno; …… 使用 Non-equi-join 要考虑清楚
Oracle Join 简介 -Examples Semi Join Example SELECT a.ename,a.hiredate FROM scott.emp a WHERE  EXISTS (SELECT 1 FROM scott.emp b WHERE a.hiredate <= b.hiredate AND  a.empno <> b.empno); Semi join  短路 Q : Semi join 最常见经典应用? A :找结果集是否存在数据
Oracle Join 简介 -Examples Anti Join Example SELECT a.ename,a.hiredate FROM scott.emp a WHERE  NOT EXISTS (SELECT 1 FROM scott.emp b WHERE a.hiredate <= b.hiredate AND  a.empno <> b.empno); Anti join  无短路 No matched results,so returned
新旧 Join 语法 –理解 SQL86 中的“ +” Oracle 把我的 + 号吃了? Table a Table b right join SELECT a.ID,a.NAME,a.code, b.ID,b.NAME,b.code FROM a,b WHERE a.ID(+)=b.ID AND a.NAME(+)=b.NAME AND TO_NUMBER(a.code)=b.code; 注意复杂外连接的写法,不要丢掉 + 号
新旧 Join 语法 –理解 SQL86 中的“ +” 加上 + 号后,执行计划为 SELECT a.ID,a.NAME,a.code, b.ID,b.NAME,b.code FROM a,b WHERE a.ID(+)=b.ID AND a.NAME(+)=b.NAME AND TO_NUMBER(a.code(+))=b.code; My god!so esay! My god!so easy!
新旧 Join 语法 –理解 SQL86 中的“ +” “ +” 到底做了什么? If A and B are joined by multiple  join conditions , then you  must  use the (+) operator in  all of these conditions .  If you do not, then Oracle will return only the rows resulting from a simple join , but without a warning or error to advise you that you do not have the results of an outer join. 理解文档中的 Join conditions 的含义很重要,文档并不详细。 可能会导致理解错误: 只要 right table 的列漏掉 + 就不是 外连接?
新旧 Join 语法 –理解 SQL86 中的“ +” Table a Table b SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.ID IS NULL;  SELECT a.ID,a.NAME,b.ID,b.NAME  FROM a,b WHERE a.ID=b.ID AND a.ID IS NULL;  SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.NAME=‘b’;  SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.NAME=b.NAME;  SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.ID(+) IS NULL;  先做外连接再过滤 内连接, join key 判断,无结果 内连接 ,“+” 无效 内连接 ,“+” 无效 一般外连接,无过滤 Table a Table b
新旧 Join 语法 –理解 SQL86 中的“ +” Conclusion: + operator 的确是 outer join 语法 如果漏掉 righ table 端条件没有写 + ,则语义上是先做 outer join, 然后做 filter 如果 righ table 端其他列选择具体值或有对应列的 join ,但是漏掉 + ,则 Oracle 会将语句转为 inner join 如果 righ table 端条件是 IS NULL ,则是先外连接,再做 filter, 当然,如果是 IS NOT NULL ,则也转为内连接 left table 的单列条件都是 filter
新旧 Join 语法 - 理解 SQL92 的 on 和 where ITPUB 问题:在两表查询时, on 和 where 到底没有没区别?  YES OR NO ? Inner Join : SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID=b.ID AND a.NAME = b.NAME; SELECT a.ID,a.NAME,b.ID,b.NAME FROM a INNER JOIN b ON a.ID=b.ID AND a.NAME = b.NAME;
新旧 Join 语法 - 理解 SQL92 的 on 和 where 对内连接来说,条件写在 on 里还是 where 里, 完全没有区别 执行计划 ---------------------------------------------------------- Plan hash value: 652036164 --------------------------------------------------------------------------- | Id  | Operation  | Name | Rows  | Bytes | Cost (%CPU)| Time  | --------------------------------------------------------------------------- |  0 | SELECT STATEMENT  |  |  2 |  20 |  7  (15)| 00:00:01 | |*  1 |  HASH JOIN  |  |  2 |  20 |  7  (15)| 00:00:01 | |  2 |  TABLE ACCESS FULL| A  |  2 |  10 |  3  (0)| 00:00:01 | |*  3 |  TABLE ACCESS FULL| B  |  3 |  15 |  3  (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access(&quot;A&quot;.&quot;ID&quot;=&quot;B&quot;.&quot;ID&quot; AND &quot;A&quot;.&quot;NAME&quot;=&quot;B&quot;.&quot;NAME&quot;) 3 - filter(&quot;B&quot;.&quot;ID&quot; IS NOT NULL)
新旧 Join 语法 - 理解 SQL92 的 on 和 where 92 语法的 on 和 where 的区别,主要体现在 Outer Join 中 SELECT * FROM hr.departments dept RIGHT JOIN  hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle';
新旧 Join 语法 - 理解 SQL92 的 on 和 where 将前面的语句进行改写: SELECT * FROM hr.departments dept  LEFT JOIN  hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle';
新旧 Join 语法 - 理解 SQL92 的 on 和 where on 和 where 混用的情况 (1) SELECT * FROM hr.departments dept  LEFT JOIN  hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle' WHERE loc.location_id>1500 ;
新旧 Join 语法 - 理解 SQL92 的 on 和 where on 和 where 混用的情况 (2) SELECT * FROM hr.departments dept  RIGHT JOIN  hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle' WHERE loc.location_id>1500 ;
新旧 Join 语法 - 理解 SQL92 的 on 和 where Conclusion: on is join condition where is filter condition 最终解决问题  寻找合适正确的语法支撑  分析对象的属性及相互关系  明确 SQL 的目标
新旧 Join 语法 - 何时用新语法 从前面的内容可以看出 Oracle 内部还是偏向于老语法 新语法虽好,但是有的版本有 BUG 主要集中在 Outer Join 中,要做足测试工作 Inner Join 没有必要考虑新语法 要遵循团队规范 +operator 对复杂 Outer Join 有限制 + 不能与 OR 或 IN 连用、不能与子查询连用 + 要求多表外连接,一个表至多外连接另一个表 其他可以参考 SQL Reference
新旧 Join 语法 - 何时用新语法 +operator 的限制: --ORA-01719 OR 和 IN 都不可以和 + 连用 SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+)  OR  a.NAME IS NOT NULL; --OK SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+) AND a.NAME IS NOT NULL; --ORA-01799 + 不能与子查询连用 SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+)  AND  a.NAME(+) IN (SELECT c.NAME FROM test3 c) ; --OK SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+)  AND a.NAME IN (SELECT c.NAME FROM test3 c);
新旧 Join 语法 -Examples 用 +operator 改写 ANSI FULL JOIN SELECT a.ID,b.ID FROM a FULL JOIN b ON a.ID=b.ID; SELECT a.ID,b.ID FROM a,b WHERE a.ID=b.ID(+) UNION ALL SELECT a.ID,b.ID FROM a,b WHERE a.ID(+)=b.ID AND a.ID IS NULL; 分析: FULL JOIN 就是两个 left outer Join+ 去掉一边 left outer Join 中包含的 Inner Join 结果 My dear, 这个和 ANTI JOIN 好像啊
常见 Join 问题和 Tuning- 关联 update 及优化 典型的错误写法,导致性能问题,事实是错误的 UPDATE
常见 Join 问题和 Tuning- 关联 update 及优化 关联 update 在 Oracle 里不要忘记过滤条件
常见 Join 问题和 Tuning- 关联 update 及优化 普通写法需要多次访问源表,可以考虑 merge
常见 Join 问题和 Tuning- 关联 update 及优化 如果数据源表的 join key 是 preserved   key, 则可以使用 update inline view 的方法,当然还有其他限制。(  BYPASS_UJVC  hint )
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 SELECT * FROM a  WHERE a.object_name IN  (SELECT b.object_name FROM b); SELECT * FROM a  WHERE  EXISTS  (SELECT 1 FROM b WHERE a.object_name=b.object_name);
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 SELECT * FROM a  WHERE a.object_name IN  (SELECT b.object_name FROM b) OR a.object_id < (SELECT b.object_id FROM b WHERE b.object_id=80000); OR 限制
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 用 union 改写,逻辑读从 1594911 下降到 1091 SELECT * FROM a  WHERE a.object_name IN  (SELECT b.object_name FROM b) UNION SELECT * FROM a WHERE a.object_id < (SELECT b.object_id FROM b WHERE b.object_id=80000);
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 NOT IN 和 NOT EXISTS 要实现等价条件多 SELECT * FROM a  WHERE a.object_name NOT IN  (SELECT b.object_name FROM b); SELECT * FROM a  WHERE NOT EXISTS  (SELECT 1 FROM b WHERE a.object_name=b.object_name)
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 下面是 11g 下 NOT IN 计划 11g 之前的计划
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 --not anti join SELECT * FROM a  WHERE a.object_name NOT IN  (SELECT b.object_name FROM b WHERE b.object_name IS NOT NULL); --anti join SELECT * FROM a  WHERE  a.object_name IS NOT NULL  AND a.object_name NOT IN  (SELECT b.object_name FROM b WHERE  b.object_name IS NOT NULL );
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 Anti Join 同样注意 OR 的问题
常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 用 union 改写含有 or 的 anti join,filter 变为 anti join 和 union… 逻辑读减少,虽然 filter 的逻辑读也不大
常见 Join 问题和 Tuning-Set 操作转换 Minus 一定条件下的改写: anti join 、外连接 SELECT a.object_id,a.object_name FROM a MINUS SELECT b.object_id,b.object_name FROM b; SELECT a.object_id,a.object_name FROM a WHERE NOT EXISTS ( SELECT 1 FROM b  WHERE a.object_id=b.object_id  AND a.object_name=b.object_name);
常见 Join 问题和 Tuning-Set 操作转换 Intersect 一定条件下的改写: inner join 、 semi join SELECT a.object_id,a.object_name FROM a INTERSECT SELECT b.object_id,b.object_name FROM b; SELECT a.object_id,a.object_name FROM a WHERE EXISTS ( SELECT 1 FROM b  WHERE a.object_id=b.object_id  AND a.object_name=b.object_name);
 
Ad

Recommended

Kid171 chap05
Kid171 chap05
Frank S.C. Tseng
 
系統程式
系統程式
建融 黃
 
Oracle公司内部数据库培训资料
Oracle公司内部数据库培训资料
yiditushe
 
系統程式 -- 第 12 章 系統軟體實作
系統程式 -- 第 12 章 系統軟體實作
鍾誠 陳鍾誠
 
重構—改善既有程式的設計(chapter 7)
重構—改善既有程式的設計(chapter 7)
Chris Huang
 
数据处理算法设计要点
数据处理算法设计要点
thinkinlamp
 
Erlang培训
Erlang培训
liu qiang
 
twMVC#27 | C# 7.0 新功能介紹
twMVC#27 | C# 7.0 新功能介紹
twMVC
 
Hash join
Hash join
Heribertus Bramundito
 
Corba and-java
Corba and-java
afreen58
 
Join operation
Join operation
Jeeva Nanthini
 
Rmi, corba and java beans
Rmi, corba and java beans
Raghu nath
 
Oracle: Joins
Oracle: Joins
oracle content
 
Webinar Smile et WSO2
Webinar Smile et WSO2
Smile I.T is open
 
Ejb
Ejb
kikoumou
 
Sql语句的优化
Sql语句的优化
abszhanghe
 
Sql Server 高级技巧系列之三整体优化
Sql Server 高级技巧系列之三整体优化
向 翔
 
Mysql fast share
Mysql fast share
rfyiamcool
 
A.oracle 数据字典与脚本初步
A.oracle 数据字典与脚本初步
WASecurity
 
数据库性能诊断的七种武器
数据库性能诊断的七种武器
Leyi (Kamus) Zhang
 
Web3.0 与人工智能
Web3.0 与人工智能
Raullen Chai
 
1 C入門教學
1 C入門教學
Sita Liu
 
第6章 数据查询
第6章 数据查询
hanmo1988
 
C++工程实践
C++工程实践
Shuo Chen
 
ajax_onlinemad
ajax_onlinemad
Kitor23
 
搜狐Pv insight(py)技术交流
搜狐Pv insight(py)技术交流
jondynet
 
搜狐Pv insight(py)技术交流
搜狐Pv insight(py)技术交流
bj
 
Mysql introduction-and-performance-optimization
Mysql introduction-and-performance-optimization
isnull
 
数据库原理第三章
数据库原理第三章
strun
 
Huangjing renren
Huangjing renren
d0nn9n
 

More Related Content

Viewers also liked (7)

Hash join
Hash join
Heribertus Bramundito
 
Corba and-java
Corba and-java
afreen58
 
Join operation
Join operation
Jeeva Nanthini
 
Rmi, corba and java beans
Rmi, corba and java beans
Raghu nath
 
Oracle: Joins
Oracle: Joins
oracle content
 
Webinar Smile et WSO2
Webinar Smile et WSO2
Smile I.T is open
 
Ejb
Ejb
kikoumou
 

Similar to Something about oracle joins (20)

Sql语句的优化
Sql语句的优化
abszhanghe
 
Sql Server 高级技巧系列之三整体优化
Sql Server 高级技巧系列之三整体优化
向 翔
 
Mysql fast share
Mysql fast share
rfyiamcool
 
A.oracle 数据字典与脚本初步
A.oracle 数据字典与脚本初步
WASecurity
 
数据库性能诊断的七种武器
数据库性能诊断的七种武器
Leyi (Kamus) Zhang
 
Web3.0 与人工智能
Web3.0 与人工智能
Raullen Chai
 
1 C入門教學
1 C入門教學
Sita Liu
 
第6章 数据查询
第6章 数据查询
hanmo1988
 
C++工程实践
C++工程实践
Shuo Chen
 
ajax_onlinemad
ajax_onlinemad
Kitor23
 
搜狐Pv insight(py)技术交流
搜狐Pv insight(py)技术交流
jondynet
 
搜狐Pv insight(py)技术交流
搜狐Pv insight(py)技术交流
bj
 
Mysql introduction-and-performance-optimization
Mysql introduction-and-performance-optimization
isnull
 
数据库原理第三章
数据库原理第三章
strun
 
Huangjing renren
Huangjing renren
d0nn9n
 
Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储
zhen chen
 
Sql培训 (1)
Sql培训 (1)
jhao niu
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
tidesq
 
组件交互模式的非主流研究
组件交互模式的非主流研究
youalab
 
Optimzing mysql
Optimzing mysql
liufabin 66688
 
Sql语句的优化
Sql语句的优化
abszhanghe
 
Sql Server 高级技巧系列之三整体优化
Sql Server 高级技巧系列之三整体优化
向 翔
 
Mysql fast share
Mysql fast share
rfyiamcool
 
A.oracle 数据字典与脚本初步
A.oracle 数据字典与脚本初步
WASecurity
 
数据库性能诊断的七种武器
数据库性能诊断的七种武器
Leyi (Kamus) Zhang
 
Web3.0 与人工智能
Web3.0 与人工智能
Raullen Chai
 
1 C入門教學
1 C入門教學
Sita Liu
 
第6章 数据查询
第6章 数据查询
hanmo1988
 
C++工程实践
C++工程实践
Shuo Chen
 
ajax_onlinemad
ajax_onlinemad
Kitor23
 
搜狐Pv insight(py)技术交流
搜狐Pv insight(py)技术交流
jondynet
 
搜狐Pv insight(py)技术交流
搜狐Pv insight(py)技术交流
bj
 
Mysql introduction-and-performance-optimization
Mysql introduction-and-performance-optimization
isnull
 
数据库原理第三章
数据库原理第三章
strun
 
Huangjing renren
Huangjing renren
d0nn9n
 
Ria的强力后盾:rest+海量存储
Ria的强力后盾:rest+海量存储
zhen chen
 
Sql培训 (1)
Sql培训 (1)
jhao niu
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
tidesq
 
组件交互模式的非主流研究
组件交互模式的非主流研究
youalab
 
Ad

More from mysqlops (20)

The simplethebeautiful
The simplethebeautiful
mysqlops
 
Oracle数据库分析函数详解
Oracle数据库分析函数详解
mysqlops
 
Percona Live 2012PPT:mysql-security-privileges-and-user-management
Percona Live 2012PPT:mysql-security-privileges-and-user-management
mysqlops
 
Percona Live 2012PPT: introduction-to-mysql-replication
Percona Live 2012PPT: introduction-to-mysql-replication
mysqlops
 
Percona Live 2012PPT: MySQL Cluster And NDB Cluster
Percona Live 2012PPT: MySQL Cluster And NDB Cluster
mysqlops
 
Percona Live 2012PPT: MySQL Query optimization
Percona Live 2012PPT: MySQL Query optimization
mysqlops
 
Pldc2012 innodb architecture and internals
Pldc2012 innodb architecture and internals
mysqlops
 
DBA新人的述职报告
DBA新人的述职报告
mysqlops
 
分布式爬虫
分布式爬虫
mysqlops
 
MySQL应用优化实践
MySQL应用优化实践
mysqlops
 
eBay EDW元数据管理及应用
eBay EDW元数据管理及应用
mysqlops
 
基于协程的网络开发框架的设计与实现
基于协程的网络开发框架的设计与实现
mysqlops
 
eBay基于Hadoop平台的用户邮件数据分析
eBay基于Hadoop平台的用户邮件数据分析
mysqlops
 
对MySQL DBA的一些思考
对MySQL DBA的一些思考
mysqlops
 
QQ聊天系统后台架构的演化与启示
QQ聊天系统后台架构的演化与启示
mysqlops
 
腾讯即时聊天IM1.4亿在线背后的故事
腾讯即时聊天IM1.4亿在线背后的故事
mysqlops
 
分布式存储与TDDL
分布式存储与TDDL
mysqlops
 
MySQL数据库生产环境维护
MySQL数据库生产环境维护
mysqlops
 
Memcached
Memcached
mysqlops
 
DevOPS
DevOPS
mysqlops
 
The simplethebeautiful
The simplethebeautiful
mysqlops
 
Oracle数据库分析函数详解
Oracle数据库分析函数详解
mysqlops
 
Percona Live 2012PPT:mysql-security-privileges-and-user-management
Percona Live 2012PPT:mysql-security-privileges-and-user-management
mysqlops
 
Percona Live 2012PPT: introduction-to-mysql-replication
Percona Live 2012PPT: introduction-to-mysql-replication
mysqlops
 
Percona Live 2012PPT: MySQL Cluster And NDB Cluster
Percona Live 2012PPT: MySQL Cluster And NDB Cluster
mysqlops
 
Percona Live 2012PPT: MySQL Query optimization
Percona Live 2012PPT: MySQL Query optimization
mysqlops
 
Pldc2012 innodb architecture and internals
Pldc2012 innodb architecture and internals
mysqlops
 
DBA新人的述职报告
DBA新人的述职报告
mysqlops
 
分布式爬虫
分布式爬虫
mysqlops
 
MySQL应用优化实践
MySQL应用优化实践
mysqlops
 
eBay EDW元数据管理及应用
eBay EDW元数据管理及应用
mysqlops
 
基于协程的网络开发框架的设计与实现
基于协程的网络开发框架的设计与实现
mysqlops
 
eBay基于Hadoop平台的用户邮件数据分析
eBay基于Hadoop平台的用户邮件数据分析
mysqlops
 
对MySQL DBA的一些思考
对MySQL DBA的一些思考
mysqlops
 
QQ聊天系统后台架构的演化与启示
QQ聊天系统后台架构的演化与启示
mysqlops
 
腾讯即时聊天IM1.4亿在线背后的故事
腾讯即时聊天IM1.4亿在线背后的故事
mysqlops
 
分布式存储与TDDL
分布式存储与TDDL
mysqlops
 
MySQL数据库生产环境维护
MySQL数据库生产环境维护
mysqlops
 
Ad

Something about oracle joins

  • 1. Something about Oracle Joins 丁俊 dingjun123 mailto:[email protected]
  • 2. 内容简介 Oracle Join 简介 Join 重要性 Join 的类别 Examples 新旧 Join 语法 理解 SQL86 中的“ +” 理解 SQL92 的 on 和 where 何时用新语法 Examples
  • 3. 内容简介续 常见 Join 问题和 Tuning 关联 update 及优化 Semi Join 和 Anti Join 注意点 Set 操作转换
  • 4. Oracle Join 简介 -Join 的重要性 Join 是关系型数据库的核心内容 Query 中经常使用表连接查询 DML 、 DDL 也经常使用到表连接 Oracle Join 有高效的算法作为性能支撑 Join 无处不在
  • 5. Oracle Join 简介 -Join 的类别 Join 类别 Inner Join and Outer Join Equi-Join and Non-Equi-Join Semi Join and Anti Join Self join 、 Cross Join… 能够识别 Join 和使用对应的 Join 很重要
  • 6. Oracle Join 简介 -Examples Non-equi-Join and self Join Example SELECT a.ename,b.ename, a.hiredate,b.hiredate FROM scott.emp a,scott.emp b WHERE a.hiredate <= b.hiredate AND a.empno <> b.empno; …… 使用 Non-equi-join 要考虑清楚
  • 7. Oracle Join 简介 -Examples Semi Join Example SELECT a.ename,a.hiredate FROM scott.emp a WHERE EXISTS (SELECT 1 FROM scott.emp b WHERE a.hiredate <= b.hiredate AND a.empno <> b.empno); Semi join 短路 Q : Semi join 最常见经典应用? A :找结果集是否存在数据
  • 8. Oracle Join 简介 -Examples Anti Join Example SELECT a.ename,a.hiredate FROM scott.emp a WHERE NOT EXISTS (SELECT 1 FROM scott.emp b WHERE a.hiredate <= b.hiredate AND a.empno <> b.empno); Anti join 无短路 No matched results,so returned
  • 9. 新旧 Join 语法 –理解 SQL86 中的“ +” Oracle 把我的 + 号吃了? Table a Table b right join SELECT a.ID,a.NAME,a.code, b.ID,b.NAME,b.code FROM a,b WHERE a.ID(+)=b.ID AND a.NAME(+)=b.NAME AND TO_NUMBER(a.code)=b.code; 注意复杂外连接的写法,不要丢掉 + 号
  • 10. 新旧 Join 语法 –理解 SQL86 中的“ +” 加上 + 号后,执行计划为 SELECT a.ID,a.NAME,a.code, b.ID,b.NAME,b.code FROM a,b WHERE a.ID(+)=b.ID AND a.NAME(+)=b.NAME AND TO_NUMBER(a.code(+))=b.code; My god!so esay! My god!so easy!
  • 11. 新旧 Join 语法 –理解 SQL86 中的“ +” “ +” 到底做了什么? If A and B are joined by multiple join conditions , then you must use the (+) operator in all of these conditions . If you do not, then Oracle will return only the rows resulting from a simple join , but without a warning or error to advise you that you do not have the results of an outer join. 理解文档中的 Join conditions 的含义很重要,文档并不详细。 可能会导致理解错误: 只要 right table 的列漏掉 + 就不是 外连接?
  • 12. 新旧 Join 语法 –理解 SQL86 中的“ +” Table a Table b SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.ID IS NULL; SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID=b.ID AND a.ID IS NULL; SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.NAME=‘b’; SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.NAME=b.NAME; SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID(+)=b.ID AND a.ID(+) IS NULL; 先做外连接再过滤 内连接, join key 判断,无结果 内连接 ,“+” 无效 内连接 ,“+” 无效 一般外连接,无过滤 Table a Table b
  • 13. 新旧 Join 语法 –理解 SQL86 中的“ +” Conclusion: + operator 的确是 outer join 语法 如果漏掉 righ table 端条件没有写 + ,则语义上是先做 outer join, 然后做 filter 如果 righ table 端其他列选择具体值或有对应列的 join ,但是漏掉 + ,则 Oracle 会将语句转为 inner join 如果 righ table 端条件是 IS NULL ,则是先外连接,再做 filter, 当然,如果是 IS NOT NULL ,则也转为内连接 left table 的单列条件都是 filter
  • 14. 新旧 Join 语法 - 理解 SQL92 的 on 和 where ITPUB 问题:在两表查询时, on 和 where 到底没有没区别? YES OR NO ? Inner Join : SELECT a.ID,a.NAME,b.ID,b.NAME FROM a,b WHERE a.ID=b.ID AND a.NAME = b.NAME; SELECT a.ID,a.NAME,b.ID,b.NAME FROM a INNER JOIN b ON a.ID=b.ID AND a.NAME = b.NAME;
  • 15. 新旧 Join 语法 - 理解 SQL92 的 on 和 where 对内连接来说,条件写在 on 里还是 where 里, 完全没有区别 执行计划 ---------------------------------------------------------- Plan hash value: 652036164 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 20 | 7 (15)| 00:00:01 | |* 1 | HASH JOIN | | 2 | 20 | 7 (15)| 00:00:01 | | 2 | TABLE ACCESS FULL| A | 2 | 10 | 3 (0)| 00:00:01 | |* 3 | TABLE ACCESS FULL| B | 3 | 15 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access(&quot;A&quot;.&quot;ID&quot;=&quot;B&quot;.&quot;ID&quot; AND &quot;A&quot;.&quot;NAME&quot;=&quot;B&quot;.&quot;NAME&quot;) 3 - filter(&quot;B&quot;.&quot;ID&quot; IS NOT NULL)
  • 16. 新旧 Join 语法 - 理解 SQL92 的 on 和 where 92 语法的 on 和 where 的区别,主要体现在 Outer Join 中 SELECT * FROM hr.departments dept RIGHT JOIN hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle';
  • 17. 新旧 Join 语法 - 理解 SQL92 的 on 和 where 将前面的语句进行改写: SELECT * FROM hr.departments dept LEFT JOIN hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle';
  • 18. 新旧 Join 语法 - 理解 SQL92 的 on 和 where on 和 where 混用的情况 (1) SELECT * FROM hr.departments dept LEFT JOIN hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle' WHERE loc.location_id>1500 ;
  • 19. 新旧 Join 语法 - 理解 SQL92 的 on 和 where on 和 where 混用的情况 (2) SELECT * FROM hr.departments dept RIGHT JOIN hr.locations loc ON dept.location_id=loc.location_id AND loc.city='Seattle' WHERE loc.location_id>1500 ;
  • 20. 新旧 Join 语法 - 理解 SQL92 的 on 和 where Conclusion: on is join condition where is filter condition 最终解决问题 寻找合适正确的语法支撑 分析对象的属性及相互关系 明确 SQL 的目标
  • 21. 新旧 Join 语法 - 何时用新语法 从前面的内容可以看出 Oracle 内部还是偏向于老语法 新语法虽好,但是有的版本有 BUG 主要集中在 Outer Join 中,要做足测试工作 Inner Join 没有必要考虑新语法 要遵循团队规范 +operator 对复杂 Outer Join 有限制 + 不能与 OR 或 IN 连用、不能与子查询连用 + 要求多表外连接,一个表至多外连接另一个表 其他可以参考 SQL Reference
  • 22. 新旧 Join 语法 - 何时用新语法 +operator 的限制: --ORA-01719 OR 和 IN 都不可以和 + 连用 SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+) OR a.NAME IS NOT NULL; --OK SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+) AND a.NAME IS NOT NULL; --ORA-01799 + 不能与子查询连用 SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+) AND a.NAME(+) IN (SELECT c.NAME FROM test3 c) ; --OK SELECT * FROM test1 a,test2 b WHERE a.ID=b.id(+) AND a.NAME IN (SELECT c.NAME FROM test3 c);
  • 23. 新旧 Join 语法 -Examples 用 +operator 改写 ANSI FULL JOIN SELECT a.ID,b.ID FROM a FULL JOIN b ON a.ID=b.ID; SELECT a.ID,b.ID FROM a,b WHERE a.ID=b.ID(+) UNION ALL SELECT a.ID,b.ID FROM a,b WHERE a.ID(+)=b.ID AND a.ID IS NULL; 分析: FULL JOIN 就是两个 left outer Join+ 去掉一边 left outer Join 中包含的 Inner Join 结果 My dear, 这个和 ANTI JOIN 好像啊
  • 24. 常见 Join 问题和 Tuning- 关联 update 及优化 典型的错误写法,导致性能问题,事实是错误的 UPDATE
  • 25. 常见 Join 问题和 Tuning- 关联 update 及优化 关联 update 在 Oracle 里不要忘记过滤条件
  • 26. 常见 Join 问题和 Tuning- 关联 update 及优化 普通写法需要多次访问源表,可以考虑 merge
  • 27. 常见 Join 问题和 Tuning- 关联 update 及优化 如果数据源表的 join key 是 preserved key, 则可以使用 update inline view 的方法,当然还有其他限制。( BYPASS_UJVC hint )
  • 28. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 SELECT * FROM a WHERE a.object_name IN (SELECT b.object_name FROM b); SELECT * FROM a WHERE EXISTS (SELECT 1 FROM b WHERE a.object_name=b.object_name);
  • 29. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 SELECT * FROM a WHERE a.object_name IN (SELECT b.object_name FROM b) OR a.object_id < (SELECT b.object_id FROM b WHERE b.object_id=80000); OR 限制
  • 30. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 用 union 改写,逻辑读从 1594911 下降到 1091 SELECT * FROM a WHERE a.object_name IN (SELECT b.object_name FROM b) UNION SELECT * FROM a WHERE a.object_id < (SELECT b.object_id FROM b WHERE b.object_id=80000);
  • 31. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 NOT IN 和 NOT EXISTS 要实现等价条件多 SELECT * FROM a WHERE a.object_name NOT IN (SELECT b.object_name FROM b); SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE a.object_name=b.object_name)
  • 32. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 下面是 11g 下 NOT IN 计划 11g 之前的计划
  • 33. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 --not anti join SELECT * FROM a WHERE a.object_name NOT IN (SELECT b.object_name FROM b WHERE b.object_name IS NOT NULL); --anti join SELECT * FROM a WHERE a.object_name IS NOT NULL AND a.object_name NOT IN (SELECT b.object_name FROM b WHERE b.object_name IS NOT NULL );
  • 34. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 Anti Join 同样注意 OR 的问题
  • 35. 常见 Join 问题和 Tuning- Semi Join 和 Anti Join 注意点 用 union 改写含有 or 的 anti join,filter 变为 anti join 和 union… 逻辑读减少,虽然 filter 的逻辑读也不大
  • 36. 常见 Join 问题和 Tuning-Set 操作转换 Minus 一定条件下的改写: anti join 、外连接 SELECT a.object_id,a.object_name FROM a MINUS SELECT b.object_id,b.object_name FROM b; SELECT a.object_id,a.object_name FROM a WHERE NOT EXISTS ( SELECT 1 FROM b WHERE a.object_id=b.object_id AND a.object_name=b.object_name);
  • 37. 常见 Join 问题和 Tuning-Set 操作转换 Intersect 一定条件下的改写: inner join 、 semi join SELECT a.object_id,a.object_name FROM a INTERSECT SELECT b.object_id,b.object_name FROM b; SELECT a.object_id,a.object_name FROM a WHERE EXISTS ( SELECT 1 FROM b WHERE a.object_id=b.object_id AND a.object_name=b.object_name);
  • 38.  

Editor's Notes

  • #8: Semi join 有短路功能 :意思是不全找,找到就 OK ,只要对应每行,查找到匹配的记录,则返回,然后继续下一次查找 Semi join 的一个实际使用,找结果集是否存在数据: select count(*) from dual where exists (select 1 from …); Semi join 也可以使用三大 join 算法, OK ,有 nested loops semi,hash joins semi and merge joins semin……. 以及对应的 hint 控制
  • #9: Anti Join 无短路 ,必须对应每行,与对子查询表全部查询一遍,然后无匹配,则返回此行记录,有匹配不返回 和 Semi join 一样,它也有三大算法以及对应的 hint 控制
  • #10: -- 检查执行计划,未发现 outer, 检查谓词未发现 + -- 语法级 SQL 转换 DROP TABLE a ; DROP TABLE b ; CREATE TABLE a (ID NUMBER,NAME VARCHAR2( 10 ), code VARCHAR2( 10 )); INSERT INTO a VALUES( 1 ,&apos;a&apos;,&apos;00001&apos;); INSERT INTO a VALUES( 2 ,&apos;b&apos;,&apos;00002&apos;); CREATE TABLE b (ID NUMBER,NAME VARCHAR2( 10 ), code NUMBER( 10 )); INSERT INTO b SELECT * FROM a ; INSERT INTO b VALUES( 3 ,&apos;c&apos;, 3 ); COMMIT; SELECT * FROM a ; SELECT * FROM b ; dingjun123@ORADB&gt; SELECT a.ID,a.NAME,a.code, 2 b.ID,b.NAME,b.code 3 FROM a,b 4 WHERE a.ID(+)=b.ID AND a.NAME(+)=b.NAME 5 AND TO_NUMBER(a.code)=b.code; 已选择 2 行。 已用时间 : 00: 00: 00.01 执行计划 ---------------------------------------------------------- Plan hash value: 652036164 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 60 | 7 (15)| 00:00:01 | |* 1 | HASH JOIN | | 1 | 60 | 7 (15)| 00:00:01 | | 2 | TABLE ACCESS FULL| A | 2 | 54 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| B | 3 | 99 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access(&amp;quot;A&amp;quot;.&amp;quot;ID&amp;quot;=&amp;quot;B&amp;quot;.&amp;quot;ID&amp;quot; AND &amp;quot;A&amp;quot;.&amp;quot;NAME&amp;quot;=&amp;quot;B&amp;quot;.&amp;quot;NAME&amp;quot; AND &amp;quot;B&amp;quot;.&amp;quot;CODE&amp;quot;=TO_NUMBER(&amp;quot;A&amp;quot;.&amp;quot;CODE&amp;quot;)) Note ----- - dynamic sampling used for this statement (level=2) 统计信息 ---------------------------------------------------------- 7 recursive calls 0 db block gets 31 consistent gets 0 physical reads 0 redo size 775 bytes sent via SQL*Net to client 416 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 2 rows processed dingjun123@ORADB&gt; SELECT a.ID,a.NAME,a.code, 2 b.ID,b.NAME,b.code 3 FROM a,b 4 WHERE a.ID(+)=b.ID AND a.NAME(+)=b.NAME 5 AND TO_NUMBER(a.code(+))=b.code; 已选择 3 行。 已用时间 : 00: 00: 00.01 执行计划 ---------------------------------------------------------- Plan hash value: 843196925 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3 | 180 | 7 (15)| 00:00:01 | |* 1 | HASH JOIN OUTER | | 3 | 180 | 7 (15)| 00:00:01 | | 2 | TABLE ACCESS FULL| B | 3 | 99 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| A | 2 | 54 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access(&amp;quot;A&amp;quot;.&amp;quot;ID&amp;quot;(+)=&amp;quot;B&amp;quot;.&amp;quot;ID&amp;quot; AND &amp;quot;A&amp;quot;.&amp;quot;NAME&amp;quot;(+)=&amp;quot;B&amp;quot;.&amp;quot;NAME&amp;quot; AND &amp;quot;B&amp;quot;.&amp;quot;CODE&amp;quot;=TO_NUMBER(&amp;quot;A&amp;quot;.&amp;quot;CODE&amp;quot;(+)))
  • #13: -- 理解 +, Conclusion : + operator 的确是 outer join 语法 如果漏掉 righ table 端条件没有写 + ,则语义上是先做 outer join, 然后做 filter 如果 righ table 端其他列选择具体值或有对应列的 join ,但是漏掉 + ,则 Oracle 会将语句转为 inner join 如果 righ table 端条件是 IS NULL ,则是先外连接,再做 filter , 当然,如果是 IS NOT NULL ,则也转为内连接 Left table 的单列条件都是 filter INSERT INTO a VALUES(NULL,&apos;c&apos;, 3 ); SELECT * FROM a ; SELECT * FROM b ; --1. 是先进行 id 的外连接,然后按 a.id is null 过滤,类似于 anti join ,找没有匹配到的 b 表记录 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .ID IS NULL; ALTER TABLE a DROP CONSTRAINTS uk_a ; ALTER TABLE b DROP CONSTRAINTS uk_b ; ALTER TABLE b MODIFY ID NULL; SELECT a .ID FROM a , b WHERE a .ID(+)= b .ID AND a .ID IS NULL; SELECT a .ID, a .NAME FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE a .ID= b .ID); -- 肯定不返回行,因为是内连接,而且是连接键 IS NULL SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID= b .ID AND a .ID IS NULL; -- 一般内连接, + 无效 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .NAME=&apos;b&apos;; -- 一般内连接, + 无效 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .NAME= b .NAME; -- 外连接 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .ID(+) IS NULL; --DELETE FROM a WHERE ID=3; -- 列换为 name INSERT INTO a VALUES( 3 ,NULL, 3 ); INSERT INTO b VALUES( 4 ,&apos;d&apos;, 4 ); SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .NAME(+) IS NULL; -- 过滤 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a RIGHT JOIN b ON a .ID= b .ID WHERE a .NAME IS NOT NULL; -- 非连接键值 , 下面两个也不同,第 1 个是先外连接后过滤 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .NAME IS NULL; SELECT a .ID, a .NAME, b .ID, b .NAME FROM a RIGHT JOIN b ON a .ID= b .ID WHERE a .NAME IS NULL; -- 第 2 个内连接 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID= b .ID AND a .NAME IS NULL; SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .ID IS NOT NULL; SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND a .NAME IS NOT NULL; --b.id&gt;1 是过滤,先过滤再连接 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND b .ID&gt; 1 ;
  • #17: 老语法改写:只能是 where 条件 1.Where dept.location_id(+)=loc.location_id AND loc.city=‘Seattle’; -- 错误,因为有过滤 2. SELECT * FROM hr . departments dept , hr . locations loc WHERE dept . location_id (+)= loc . location_id AND loc . city =CASE WHEN ( dept . location_id (+) IS NOT NULL) THEN &apos;Seattle&apos; ELSE &apos;Seattle&apos; END; Loc.city= ‘Seattle’ 的前提是前面的 location_id 条件必须匹配或不匹配,这样外连接的条件就完整了 3. 也可以这样改写 SELECT * FROM hr . departments dept , hr . locations loc WHERE loc . location_id = decode ( loc . city ,&apos;Seattle&apos;, dept . location_id (+));
  • #18: SELECT * FROM hr . departments dept LEFT JOIN hr . locations loc ON dept . location_id = loc . location_id AND loc . city =&apos;Seattle&apos;; SELECT * FROM hr . departments dept LEFT JOIN hr . locations loc ON dept . location_id = loc . location_id AND loc . city (+)=&apos;Seattle&apos;;
  • #19: 从前面 + 语法分析也可以知道,因为是对非主表过滤,所以语义上是内连接 实际计划也转为内连接,并且 Oracle 很聪明,使用了谓词传递
  • #20: 因为对主表过滤,所以是先过滤,后外连接 并且也有主表的谓词传递到了从表的谓词
  • #21: SQL 的分析步骤很重要 不进行详细分析,写不好 SQL ,甚至错误 不清楚对象的属性,约束,索引等信息,不清楚对象间的关系,写的 SQL 可能不高效 不能寻找合适正确的语法支撑,写不好 SQL 分析重于一切,必须先分析,才开始动手构造 SQL
  • #23: drop TABLE test1 ; drop TABLE test2 ; drop TABLE test3 ; CREATE TABLE test1 AS SELECT 1 id, &apos;aa&apos; NAME FROM dual UNION ALL SELECT 2 , &apos;bb&apos; FROM dual UNION ALL SELECT 3 , &apos;cc&apos; FROM dual ; CREATE TABLE test2 AS SELECT 1 id, &apos;aa&apos; NAME FROM dual UNION ALL SELECT 4 , &apos;dd&apos; FROM dual ; CREATE TABLE test3 AS SELECT 2 id, &apos;bb&apos; NAME FROM dual UNION ALL SELECT 5 , &apos;ee&apos; FROM dual ; SELECT * FROM test1 a , test2 b WHERE a .ID= b .id(+) OR a .NAME IS NOT NULL; SELECT * FROM test1 a , test2 b WHERE a .ID= b .id(+) AND a .NAME IS NOT NULL; SELECT * FROM test1 a , test2 b WHERE a .ID= b .id(+) AND a .NAME(+) IN (SELECT c .NAME FROM test3 c ); SELECT * FROM test1 a , test2 b WHERE a .ID= b .id(+) AND a .NAME IN (SELECT c .NAME FROM test3 c ); SELECT * FROM test1 a , test2 b , test3 c WHERE a .id = b .id(+) AND a .id = c .id(+); DROP TABLE test1 ; DROP TABLE test2 ; DROP TABLE test3 ; CREATE TABLE test1 AS SELECT 1 id, &apos;aa&apos; NAME FROM dual UNION ALL SELECT 2 , &apos;bb&apos; FROM dual ; CREATE TABLE test2 AS SELECT 1 id, &apos;aa&apos; NAME FROM dual UNION ALL SELECT 4 , &apos;dd&apos; FROM dual ; CREATE TABLE test3 AS SELECT 2 id, &apos;bb&apos; NAME FROM dual UNION ALL SELECT 1 , &apos;aa&apos; FROM dual UNION ALL SELECT 4 , &apos;dd&apos; FROM dual UNION ALL SELECT 5 , &apos;ee&apos; FROM dual ; --0ra-01417 一个表最多能外连接到一个表 -- 不知道谁才是基表 SELECT * FROM test1 a , test2 b , test3 c WHERE a .id(+) = b .id AND a .id(+) = c .id; -- 用中间结果集改写 SELECT * FROM (SELECT a .id aid , a .NAME aname , b .id bid , b .NAME bname FROM test1 a , test2 b WHERE a .id(+) = b .id) c , test3 d WHERE c . aid (+) = d .id; -- 基表最终是 test3 SELECT * FROM test1 a RIGHT JOIN test2 b ON a .ID = b .ID RIGHT JOIN test3 c ON a .ID = c .ID; -- 基表最终是 test2 SELECT * FROM test1 a RIGHT JOIN test3 b ON a .ID = b .ID RIGHT JOIN test2 c ON a .ID = c .ID;
  • #24: a.ID IS NULL 不能写成 a.ID(+) IS NULL, 习惯选择一个连接键判断,其他的也可以,比如 rowid DROP TABLE a ; DROP TABLE b ; CREATE TABLE a (ID NUMBER,NAME VARCHAR2( 10 )); CREATE TABLE b (ID NUMBER,NAME VARCHAR2( 10 )); INSERT INTO a VALUES( 1 ,&apos;a&apos;); INSERT INTO a VALUES( 2 ,&apos;b&apos;); INSERT INTO a VALUES( 3 ,&apos;c&apos;); INSERT INTO b VALUES( 1 ,&apos;a&apos;); INSERT INTO b VALUES( 2 ,&apos;b&apos;); INSERT INTO b VALUES( 4 ,&apos;d&apos;); COMMIT ; SELECT * FROM a ; SELECT * FROM b ; SELECT a .ID, b .ID FROM a FULL JOIN b ON a .ID= b .ID; -- 如果连接条件 1:1, 可以用 union SELECT a .ID, b .ID FROM a , b WHERE a .ID= b .ID(+) UNION SELECT a .ID, b .ID FROM a , b WHERE a .ID(+)= b .ID; -- 非 1:1 应该用 UNION ALL 并且第 2 个语句在 + 号处的列只选出 NULL 看到有些人经常问,把自己的一些体会简单举个例子,详细的东西还是需要自己慢慢体会的,比如 from a , b where a .id= b .id(+) and b .name=&apos;a&apos; 这个 b . name 没有 + 号则相当于普通的内连接 ( 先外连接后过滤,这个准确点,比如 b .name is null 那么和普通内连接还是有所不同的 ) , -- 用连接键 is null 判断,不用考虑 null 问题,因为选择的是不配的值, rowid is null 也可以,他非连接键只能选择 NOT NULL 约束的 ?? 不对, 这个是过滤,所有没有匹配列都为 NULL INSERT INTO a VALUES( 1 ,&apos;a&apos;); SELECT a .ID, b .ID FROM a , b WHERE a .ID= b .ID(+) UNION ALL SELECT a .ID, b .ID FROM a , b WHERE a .ID(+)= b .ID AND a .ID IS NULL; -- 下面的是找以 id 连接在 b 中不在 a 中的数据,相当于 SELECT a .ID, b .ID FROM a , b WHERE a .ID(+)= b .ID AND a .ID IS NULL; ==&gt; SELECT a .ID, b .ID FROM a RIGHT JOIN b ON a .ID= b .ID WHERE a .ID IS NULL; SELECT a .ID, b .ID FROM a , b WHERE a .ID(+)= b .ID AND a .ID(+) IS NULL; ===&gt; SELECT a .ID, b .ID FROM a RIGHT JOIN b ON a .ID= b .ID AND a .ID IS NULL;
  • #25: 73993 * 3 + 295, 类似嵌套循环,效率很低,目标表必须做驱动表 全表更新 DROP TABLE a ; DROP TABLE b ; CREATE TABLE a AS SELECT * FROM all_objects ; CREATE TABLE b AS SELECT * FROM user_objects ; CREATE INDEX idx_a ON a ( object_id ); CREATE INDEX idx_b ON b ( object_id ); BEGIN dbms_stats . gather_table_stats ( ownname =&gt; USER, tabname =&gt; &apos;a&apos;,cascade =&gt; TRUE); dbms_stats . gather_table_stats ( ownname =&gt; USER, tabname =&gt; &apos;b&apos;,cascade =&gt; TRUE); END; SELECT COUNT(*) FROM a ; --73993 SELECT COUNT(*) FROM b ; --2301 UPDATE a SET a . object_name =(SELECT b . object_name FROM b WHERE a . object_id = b . object_id ); UPDATE a SET a . object_name =(SELECT b . object_name FROM b WHERE a . object_id = b . object_id ) WHERE EXISTS (SELECT 1 FROM b WHERE a . object_id = b . object_id ); MERGE INTO a USING b ON a . object_id = b . object_id WHEN MATCHED THEN UPDATE a . object_name = b . object_name ; ALTER TABLE b ADD CONSTRAINTS uk_b UNIQUE( object_id ); UPDATE (SELECT a . object_name aname , b . object_name bname FROM a , b WHERE a . object_id = b . object_id ) SET aname = bname ; SELECT 73993 * 3 + 295 FROM dual ;
  • #26: 多次访问 B 表
  • #27: Merge 一般不错,但是 merge 也有限制,比如连接条件不能返回多行
  • #28: BYPASS_UJVC 11g 不能用了 必须保证 b 的连接键值有唯一约束 为了保证视图是可更新的,其定义中不 能包含以下语法结构( construct ): ● 集合操作符( set operator ) ● DISTINCT 操作符 ● 聚合函数( aggregate function )或分析型函数( analytic function ) ● GROUP BY , ORDER BY , CONNECT BY ,或 START WITH 字句 ● 在 SELECT 之后的列表中使用 collection expression ● 在 SELECT 之后的列表中使用子查询( subquery ) ● 连接( join )(但是有例外情况)
  • #29: In 和 exists 等价,因为 in 没有 null 的问题,从计划可以看出来
  • #30: Exists 一样,只要 semi join 与 or 连用就走不了 semi,filter 效率经常低
  • #32: ALTER TABLE a MODIFY object_name NULL; ALTER TABLE b MODIFY object_name NULL; 对于 not exists 是 anti join,not in 主要看前后的都要有 not null 约束 SELECT * FROM a WHERE a . object_name NOT IN (SELECT b . object_name FROM b ); SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE a . object_name = b . object_name ) SELECT * FROM a WHERE a . object_name NOT IN (SELECT b . object_name FROM b WHERE b . object_name IS NOT NULL); -- 这个菜是 anti join SELECT * FROM a WHERE a . object_name IS NOT NULL AND a . object_name NOT IN (SELECT b . object_name FROM b WHERE b . object_name IS NOT NULL);
  • #36: ALTER TABLE a MODIFY object_name NULL; ALTER TABLE b MODIFY object_name NULL; SELECT * FROM a WHERE a . object_name IN (SELECT b . object_name FROM b ); SELECT * FROM a WHERE EXISTS (SELECT 1 FROM b WHERE a . object_name = b . object_name ) OR a . object_id &lt; 80000 ; SELECT * FROM a WHERE a . object_name IN (SELECT b . object_name FROM b ) OR a . object_id &lt; 80000 ; SELECT * FROM a WHERE a . object_name IN (SELECT b . object_name FROM b ) OR a . object_id &lt; (SELECT b . object_id FROM b WHERE b . object_id = 80000 ); SELECT * FROM a WHERE a . object_name IN (SELECT b . object_name FROM b ) UNION SELECT * FROM a WHERE a . object_id &lt; (SELECT b . object_id FROM b WHERE b . object_id = 80000 ); /*+precompute_subquery */ SELECT * FROM a WHERE a . object_name NOT IN (SELECT b . object_name FROM b ); SELECT /*+optimizer_features_enable(&apos;9.0.0&apos;)*/ * FROM a WHERE a . object_name NOT IN (SELECT b . object_name FROM b ); SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE a . object_name = b . object_name ) SELECT * FROM a WHERE a . object_name NOT IN (SELECT b . object_name FROM b WHERE b . object_name IS NOT NULL); -- 这个菜是 anti join SELECT * FROM a WHERE a . object_name IS NOT NULL AND a . object_name NOT IN (SELECT b . object_name FROM b WHERE b . object_name IS NOT NULL); SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE a . object_name = b . object_name ) OR a . object_id &lt; 80000 ; SELECT * FROM a WHERE NOT EXISTS (SELECT 1 FROM b WHERE a . object_name = b . object_name ) UNION SELECT * FROM a WHERE a . object_id &lt; 80000 ;
  • #37: 一定情况下也可以考虑外连接
  • #38: 当然也可以通过索引来消除排序 就是相互匹配