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);
 

More Related Content

PDF
Kid171 chap05
PDF
系統程式
PPT
Oracle公司内部数据库培训资料
DOCX
系統程式 -- 第 12 章 系統軟體實作
PPTX
重構—改善既有程式的設計(chapter 7)
PPT
数据处理算法设计要点
ODP
Erlang培训
PDF
twMVC#27 | C# 7.0 新功能介紹
Kid171 chap05
系統程式
Oracle公司内部数据库培训资料
系統程式 -- 第 12 章 系統軟體實作
重構—改善既有程式的設計(chapter 7)
数据处理算法设计要点
Erlang培训
twMVC#27 | C# 7.0 新功能介紹

Viewers also liked (7)

DOCX
PPT
Corba and-java
PPTX
Join operation
PPTX
Rmi, corba and java beans
PPTX
Oracle: Joins
PDF
Webinar Smile et WSO2
PPTX
Ejb
Corba and-java
Join operation
Rmi, corba and java beans
Oracle: Joins
Webinar Smile et WSO2
Ejb
Ad

Similar to Something about oracle joins (20)

PPT
Sql语句的优化
PPT
Sql Server 高级技巧系列之三整体优化
PDF
Mysql fast share
PDF
A.oracle 数据字典与脚本初步
PPT
数据库性能诊断的七种武器
PPT
Web3.0 与人工智能
PPT
1 C入門教學
PPT
第6章 数据查询
PDF
C++工程实践
PPT
ajax_onlinemad
PPT
搜狐Pv insight(py)技术交流
 
PPT
搜狐Pv insight(py)技术交流
PPT
Mysql introduction-and-performance-optimization
PPT
数据库原理第三章
PPT
Huangjing renren
PDF
Ria的强力后盾:rest+海量存储
PDF
Sql培训 (1)
PPTX
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
PPT
组件交互模式的非主流研究
PPT
Optimzing mysql
Sql语句的优化
Sql Server 高级技巧系列之三整体优化
Mysql fast share
A.oracle 数据字典与脚本初步
数据库性能诊断的七种武器
Web3.0 与人工智能
1 C入門教學
第6章 数据查询
C++工程实践
ajax_onlinemad
搜狐Pv insight(py)技术交流
 
搜狐Pv insight(py)技术交流
Mysql introduction-and-performance-optimization
数据库原理第三章
Huangjing renren
Ria的强力后盾:rest+海量存储
Sql培训 (1)
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
组件交互模式的非主流研究
Optimzing mysql
Ad

More from mysqlops (20)

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

Something about oracle joins

  • 1. Something about Oracle Joins 丁俊 dingjun123 mailto:dingjunlove@163.com
  • 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: 当然也可以通过索引来消除排序 就是相互匹配