Something about oracle joins
Upcoming SlideShare
Loading in...5
×
 

Something about oracle joins

on

  • 1,991 views

Oracle,JOIN,SQL,OPTIMIZE

Oracle,JOIN,SQL,OPTIMIZE

Statistics

Views

Total Views
1,991
Views on SlideShare
1,698
Embed Views
293

Actions

Likes
0
Downloads
21
Comments
0

1 Embed 293

http://www.mysqlops.com 293

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • 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 控制
  • Anti Join 无短路 ,必须对应每行,与对子查询表全部查询一遍,然后无匹配,则返回此行记录,有匹配不返回 和 Semi join 一样,它也有三大算法以及对应的 hint 控制
  • -- 检查执行计划,未发现 outer, 检查谓词未发现 + -- 语法级 SQL 转换 DROP TABLE a ; DROP TABLE b ; CREATE TABLE a (ID NUMBER,NAME VARCHAR2( 10 ), code VARCHAR2( 10 )); INSERT INTO a VALUES( 1 ,'a','00001'); INSERT INTO a VALUES( 2 ,'b','00002'); CREATE TABLE b (ID NUMBER,NAME VARCHAR2( 10 ), code NUMBER( 10 )); INSERT INTO b SELECT * FROM a ; INSERT INTO b VALUES( 3 ,'c', 3 ); COMMIT; SELECT * FROM a ; SELECT * FROM b ; dingjun123@ORADB> 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("A"."ID"="B"."ID" AND "A"."NAME"="B"."NAME" AND "B"."CODE"=TO_NUMBER("A"."CODE")) 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> 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("A"."ID"(+)="B"."ID" AND "A"."NAME"(+)="B"."NAME" AND "B"."CODE"=TO_NUMBER("A"."CODE"(+)))
  • -- 理解 +, 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,'c', 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='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; --DELETE FROM a WHERE ID=3; -- 列换为 name INSERT INTO a VALUES( 3 ,NULL, 3 ); INSERT INTO b VALUES( 4 ,'d', 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>1 是过滤,先过滤再连接 SELECT a .ID, a .NAME, b .ID, b .NAME FROM a , b WHERE a .ID(+)= b .ID AND b .ID> 1 ;
  • 老语法改写:只能是 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 'Seattle' ELSE 'Seattle' END; Loc.city= ‘Seattle’ 的前提是前面的 location_id 条件必须匹配或不匹配,这样外连接的条件就完整了 3. 也可以这样改写 SELECT * FROM hr . departments dept , hr . locations loc WHERE loc . location_id = decode ( loc . city ,'Seattle', dept . location_id (+));
  • SELECT * FROM hr . departments dept LEFT JOIN hr . locations loc ON dept . location_id = loc . location_id AND loc . city ='Seattle'; SELECT * FROM hr . departments dept LEFT JOIN hr . locations loc ON dept . location_id = loc . location_id AND loc . city (+)='Seattle';
  • 从前面 + 语法分析也可以知道,因为是对非主表过滤,所以语义上是内连接 实际计划也转为内连接,并且 Oracle 很聪明,使用了谓词传递
  • 因为对主表过滤,所以是先过滤,后外连接 并且也有主表的谓词传递到了从表的谓词
  • SQL 的分析步骤很重要 不进行详细分析,写不好 SQL ,甚至错误 不清楚对象的属性,约束,索引等信息,不清楚对象间的关系,写的 SQL 可能不高效 不能寻找合适正确的语法支撑,写不好 SQL 分析重于一切,必须先分析,才开始动手构造 SQL
  • drop TABLE test1 ; drop TABLE test2 ; drop TABLE test3 ; CREATE TABLE test1 AS SELECT 1 id, 'aa' NAME FROM dual UNION ALL SELECT 2 , 'bb' FROM dual UNION ALL SELECT 3 , 'cc' FROM dual ; CREATE TABLE test2 AS SELECT 1 id, 'aa' NAME FROM dual UNION ALL SELECT 4 , 'dd' FROM dual ; CREATE TABLE test3 AS SELECT 2 id, 'bb' NAME FROM dual UNION ALL SELECT 5 , 'ee' 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, 'aa' NAME FROM dual UNION ALL SELECT 2 , 'bb' FROM dual ; CREATE TABLE test2 AS SELECT 1 id, 'aa' NAME FROM dual UNION ALL SELECT 4 , 'dd' FROM dual ; CREATE TABLE test3 AS SELECT 2 id, 'bb' NAME FROM dual UNION ALL SELECT 1 , 'aa' FROM dual UNION ALL SELECT 4 , 'dd' FROM dual UNION ALL SELECT 5 , 'ee' 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;
  • 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 ,'a'); INSERT INTO a VALUES( 2 ,'b'); INSERT INTO a VALUES( 3 ,'c'); INSERT INTO b VALUES( 1 ,'a'); INSERT INTO b VALUES( 2 ,'b'); INSERT INTO b VALUES( 4 ,'d'); 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='a' 这个 b . name 没有 + 号则相当于普通的内连接 ( 先外连接后过滤,这个准确点,比如 b .name is null 那么和普通内连接还是有所不同的 ) , -- 用连接键 is null 判断,不用考虑 null 问题,因为选择的是不配的值, rowid is null 也可以,他非连接键只能选择 NOT NULL 约束的 ?? 不对, 这个是过滤,所有没有匹配列都为 NULL INSERT INTO a VALUES( 1 ,'a'); 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; ==> 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; ===> SELECT a .ID, b .ID FROM a RIGHT JOIN b ON a .ID= b .ID AND a .ID IS NULL;
  • 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 => USER, tabname => 'a',cascade => TRUE); dbms_stats . gather_table_stats ( ownname => USER, tabname => 'b',cascade => 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 ;
  • 多次访问 B 表
  • Merge 一般不错,但是 merge 也有限制,比如连接条件不能返回多行
  • 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 )(但是有例外情况)
  • In 和 exists 等价,因为 in 没有 null 的问题,从计划可以看出来
  • Exists 一样,只要 semi join 与 or 连用就走不了 semi,filter 效率经常低
  • 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);
  • 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 < 80000 ; SELECT * FROM a WHERE a . object_name IN (SELECT b . object_name FROM b ) OR a . object_id < 80000 ; 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 ); 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 ); /*+precompute_subquery */ SELECT * FROM a WHERE a . object_name NOT IN (SELECT b . object_name FROM b ); SELECT /*+optimizer_features_enable('9.0.0')*/ * 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 < 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 < 80000 ;
  • 一定情况下也可以考虑外连接
  • 当然也可以通过索引来消除排序 就是相互匹配

Something about oracle joins Something about oracle joins Presentation Transcript

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