今天开发过程中遇到了一个很麻烦的外连接的问题,到最后都没有找到很好的解决方法,最后只能用union all 实现了,虽然性能不比外连接,但至少拓展了外连接的局限性。
首先给出我测试用的三个表和数据(左右外连接道理是一样的,我只总结了左连接):
KC21表: create table KC21 ( AKB020 VARCHAR2(14) not null, AKC190 VARCHAR2(18) not null, AAC001 VARCHAR2(20) not null ); INSERT INTO KC21 (AKB020, AKC190, AAC001) VALUES('110', '266', '1302012062942'); INSERT INTO KC21 (AKB020, AKC190, AAC001) VALUES('456', '369', '1302012063210'); INSERT INTO KC21 (AKB020, AKC190, AAC001) VALUES('1000', '472', '1302012045811'); INSERT INTO KC21 (AKB020, AKC190, AAC001) VALUES('123', '335', '1302012063275'); KC24表: create table KC24 ( AKB020 VARCHAR2(14) not null, AKC190 VARCHAR2(18) not null, AAE072 VARCHAR2(20) not null ); insert into KC24 (AKB020, AKC190, AAE072)values ('110', '335', '2188038055'); insert into KC24 (AKB020, AKC190, AAE072)values ('11', '369', '2188038092'); insert into KC24 (AKB020, AKC190, AAE072)values ('1000', '472', '2188038197'); insert into KC24 (AKB020, AKC190, AAE072)values ('110', '339', '2188038058'); KB01表: create table KB01 ( AKB020 VARCHAR2(14) not null, AKB021 VARCHAR2(50) ); insert into KB01 (AKB020, AKB021)values ('1000', '唐山实时测试医院'); insert into KB01 (AKB020, AKB021)values ('110', '唐山大医院A'); insert into KB01 (AKB020, AKB021)values ('123', '唐山大医院B'); insert into KB01 (AKB020, AKB021)values ('456', '唐山大医院C'); insert into KB01 (AKB020, AKB021)values ('11', '唐山大医院D');oracle官方提供了两种方来实现外连接,一种是在from子句中使用left outer join/right outer join/full outer join,另外一种是在where子句中使用大家都比较熟悉的符号“(+)”,这里我想写一下我对这两个方法的理解。
一、使用一个连接条件的外连接:
SQL> select * from kc21 left outer join kc24 on kc21.akb020=kc24.akb020; AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 110 266 1302012062942 110 335 2188038055 1000 472 1302012045811 1000 472 2188038197 110 266 1302012062942 110 339 2188038058 123 335 1302012063275 456 369 1302012063210 SQL> select * from kc21 ,kc24 where kc21.akb020=kc24.akb020(+); AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 110 266 1302012062942 110 335 2188038055 1000 472 1302012045811 1000 472 2188038197 110 266 1302012062942 110 339 2188038058 123 335 1302012063275 456 369 1302012063210不难看出这两个语句查询的结果是一样的,因此我们可以说这两种语法是可以转化的,但是很明显第一种写法比较好理解,这也是oracle官方建议的。官方文档是这样说的:Oracle recommends that you use the FROM clause OUTER JOIN syntax rather than the Oracle join operator。
二、使用两个连接条件的情况:
SQL> select * from kc21 left outer join kc24 on (KC21.akb020=kc24.akb020 and KC21.akc190=KC24.akc190); AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 1000 472 1302012045811 1000 472 2188038197 110 266 1302012062942 123 335 1302012063275 456 369 1302012063210 SQL> select * from kc21,kc24 where kc21.akb020=kc24.akb020(+) and kc21.akc190=KC24.akc190(+); AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 1000 472 1302012045811 1000 472 2188038197 110 266 1302012062942 456 369 1302012063210 123 335 1302012063275不难看出这两个语句查询的结果也是一样的,这两个语句是等价的。意思是只有AKB020和akc190同时相等才算匹配。同样很明显还是第一个语句好理解。有时候直接给出第二个语句,我们很难想象最终会是一个什么样的结果。这里还是Oracle官方给出了我们为什么要这样写的理由:If A and B are joined by multiple join conditions, then you must use the (+) operator in all of these conditions.
SQL> SELECT A.akb020, A.AKC190, B.akb020, B.akc190, C.akb021 2 FROM kc21 A, kc24 B, kb01 C 3 WHERE A.akb020 = B.akb020(+) 4 AND A.akc190 = B.akc190(+) 5 AND A.akb020 = C.akb020; AKB020 AKC190 AKB020 AKC190 AKB021 -------------- ------------------ -------------- ------------------ -------------------------------------------------- 1000 472 1000 472 唐山实时测试医院 110 266 唐山大医院A 123 335 唐山大医院B 456 369 唐山大医院C可以看出这就好像是在第二步的基础上关联第三个表将医院编码转化为医院名称,得出这样的结果是很好理解的。
SQL> select * from kc21,kc24 where kc21.akb020=kc24.akb020(+); AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 110 266 1302012062942 110 335 2188038055 1000 472 1302012045811 1000 472 2188038197 110 266 1302012062942 110 339 2188038058 123 335 1302012063275 456 369 1302012063210 SQL> select * from kc21,kc24 where kc21.akc190=kc24.akc190; AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 123 335 1302012063275 110 335 2188038055 456 369 1302012063210 11 369 2188038092 1000 472 1302012045811 1000 472 2188038197 SQL> select * from kc21,kc24 where kc21.akb020=kc24.akb020(+) and kc21.akc190=kc24.akc190; AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 1000 472 1302012045811 1000 472 2188038197 SQL> select * from kc21,kc24 where kc21.akb020=kc24.akb020 and kc21.akc190=kc24.akc190; AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 1000 472 1302012045811 1000 472 2188038197在上面几个SQL语句用有了一个和第二个,我们很自然的能过得到第三个,这也是符合常理的,第三个结果集刚好是第一和第二的交集。现在要说明的是第三个和第四个SQL语句效果是一样的,因为在有多个连接条件的两个表在连接时如果没有都写上“(+)”,那么oracle视同简单的等值内连接。这也是oracle官方声明的:If Aand B are joined by multiple join conditions, then you must use the (+) operator in all of these conditions. If you do not, then Oracle Database 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.
五、全外连接
oracle对于全连接不支持在连接条件的左右都加“(+)”,只有一种方式,就是在from子句中使用full outer join ,在on子句中加连接条件:
SQL> select * from kc21 full outer join kc24 on (kc21.akb020=KC24.akb020); AKB020 AKC190 AAC001 AKB020 AKC190 AAE072 -------------- ------------------ -------------------- -------------- ------------------ -------------------- 110 266 1302012062942 110 335 2188038055 1000 472 1302012045811 1000 472 2188038197 110 266 1302012062942 110 339 2188038058 123 335 1302012063275 456 369 1302012063210 11 369 2188038092 6 rows selected这个结果很好理解,满足匹配条件的作为一条成功记录返回,不满足对应另外一个表的字段都以NULL填充。