【SQL】连接 JOIN

目录

概述

内连接:INNER JOIN

外连接:OUTER JOIN

1、全连接 FULL OUT JOIN

全连接:

2、左外连接LEFT OUT JOIN

左外连接:

3、右外连接:RIGHT JOIN

右外连接

笛卡尔积:CROSS JOIN

自然连接:NATURAL JOIN

SEMI JOIN

ANTI JOIN

自连接:SELF JOIN


概述

Oracle中表之间的连接(JOIN)有很多种,当然也不仅限于表与表,还可以表与视图、物化视图等联结,以下是官方解释

 

A join is a query that combines rows from two or more tables, views, or materialized views. Oracle Database performs a join whenever multiple tables appear in the FROM clause of the query. The select list of the query can select any columns from any of these tables. If any two of these tables have a column name in common, then you must qualify all references to these columns throughout the query with table names to avoid ambiguity.

 

JOIN有多种方式,分类如下图所示

 

内连接:INNER JOIN

内连接又称简单连接或等值连接

例如想知道每位员工所在部门的名称

查看员工表emp和部门表dept

SQL> select * from emp;
 
EMPNO ENAME   JOB          MGR HIREDATE              SAL  COMM DEPTNO

----- ------- ---------- ----- ------------------- ----- ----- ------
 7369 SMITH   CLERK       7902 1980-12-17 00:00:00   800           20
 7499 ALLEN   SALESMAN    7698 1981-02-20 00:00:00  1600   300     30
 7521 WARD    SALESMAN    7698 1981-02-22 00:00:00  1250   500     30
 7566 JONES   MANAGER     7839 1981-04-02 00:00:00  2975           20
 7654 MARTIN  SALESMAN    7698 1981-09-28 00:00:00  1250  1400     30
 7698 BLAKE   MANAGER     7839 1981-05-01 00:00:00  2850           30
 7782 CLARK   MANAGER     7839 1981-06-09 00:00:00  2450           10
 7788 SCOTT   ANALYST     7566 1987-04-19 00:00:00  3000           20
 7839 KING    PRESIDENT        1981-11-17 00:00:00  5000           10
 7844 TURNER  SALESMAN    7698 1981-09-08 00:00:00  1500     0     30
 7876 ADAMS   CLERK       7788 1987-05-23 00:00:00  1100           20
 7900 JAMES   CLERK       7698 1981-12-03 00:00:00   950           30
 7902 FORD    ANALYST     7566 1981-12-03 00:00:00  3000           20
 7934 MILLER  CLERK       7782 1982-01-23 00:00:00  1300           10
 
14 rows selected.
 

SQL> select * from dept;
 
DEPTNO DNAME           LOC

------ --------------- ---------------
    10 ACCOUNTING      NEW YORK
   
20 RESEARCH        DALLAS
   
30 SALES           CHICAGO
   
40 OPERATIONS      BOSTON

实现方式有三种

第一种

SQL>

select e.empno,e.ename,e.deptno,d.dname

from emp e

INNER JOIN dept d on e.deptno = d.deptno;
 
EMPNO ENAME   DEPTNO DNAME

----- ------- ------ ---------------
 7782 CLARK       10 ACCOUNTING
 
7839 KING        10 ACCOUNTING
 
7934 MILLER      10 ACCOUNTING
 
7566 JONES       20 RESEARCH
 
7902 FORD        20 RESEARCH
 
7876 ADAMS       20 RESEARCH
 
7369 SMITH       20 RESEARCH
 
7788 SCOTT       20 RESEARCH
 
7521 WARD        30 SALES
 
7844 TURNER      30 SALES
 
7499 ALLEN       30 SALES
 
7900 JAMES       30 SALES
 
7698 BLAKE       30 SALES
 
7654 MARTIN      30 SALES
 

14 rows selected.

第二种

SQL>

select e.empno,e.ename,e.deptno,d.dname

from emp e,dept d

where e.deptno=d.deptno;
 
EMPNO ENAME   DEPTNO DNAME

----- ------- ------ ---------------
 7782 CLARK       10 ACCOUNTING
 
7839 KING        10 ACCOUNTING
 
7934 MILLER      10 ACCOUNTING
 
7566 JONES       20 RESEARCH
 
7902 FORD        20 RESEARCH
 
7876 ADAMS       20 RESEARCH
 
7369 SMITH       20 RESEARCH
 
7788 SCOTT       20 RESEARCH
 
7521 WARD        30 SALES
 
7844 TURNER      30 SALES
 
7499 ALLEN       30 SALES
 
7900 JAMES       30 SALES
 
7698 BLAKE       30 SALES
 
7654 MARTIN      30 SALES
 

14 rows selected.

第三种 是用USING,但是不常用

SQL> select e.empno,e.ename,e.deptno,d.dname
from emp e
INNER JOIN dept d
USING(deptno);
select e.empno,e.ename,e.deptno,d.dname from emp e INNER JOIN dept d USING(deptno)
                       *
ERROR
at line 1:
ORA-
25154: column part of USING clause cannot have qualifier

怎么报错了?

Oracle 里用 JOIN USING 或 NATURAL JOIN 时,如果两表共有的列的名称前加上某表名作为前缀,则会报编译错误: "ORA-25154: column part of USING clause cannot have qualifier" 或 "ORA-25155: column used in NATURAL join cannot have qualifier".

把共有的列名称的前缀去掉即可

SQL> select e.empno,e.ename,deptno,d.dname
from emp e
INNER JOIN dept d
USING(deptno);
 
EMPNO ENAME   DEPTNO DNAME

----- ------- ------ ---------------
 7782 CLARK       10 ACCOUNTING
 
7839 KING        10 ACCOUNTING
 
7934 MILLER      10 ACCOUNTING
 
7566 JONES       20 RESEARCH
 
7902 FORD        20 RESEARCH
 
7876 ADAMS       20 RESEARCH
 
7369 SMITH       20 RESEARCH
 
7788 SCOTT       20 RESEARCH
 
7521 WARD        30 SALES
 
7844 TURNER      30 SALES
 
7499 ALLEN       30 SALES
 
7900 JAMES       30 SALES
 
7698 BLAKE       30 SALES
 
7654 MARTIN      30 SALES
 

14 rows selected.

INNER JOIN也可以简写成JOIN,但是建议不使用,以免造成混乱

SQL>
select e.empno,e.ename,e.deptno,d.dname
from emp e
JOIN dept d on e.deptno = d.deptno;
 
EMPNO ENAME   DEPTNO DNAME

----- ------- ------ ---------------
 7782 CLARK       10 ACCOUNTING
 
7839 KING        10 ACCOUNTING
 
7934 MILLER      10 ACCOUNTING
 
7566 JONES       20 RESEARCH
 
7902 FORD        20 RESEARCH
 
7876 ADAMS       20 RESEARCH
 
7369 SMITH       20 RESEARCH
 
7788 SCOTT       20 RESEARCH
 
7521 WARD        30 SALES
 
7844 TURNER      30 SALES
 
7499 ALLEN       30 SALES
 
7900 JAMES       30 SALES
 
7698 BLAKE       30 SALES
 
7654 MARTIN      30 SALES
 

14 rows selected.
 

SQL>
select e.empno,e.ename,deptno,d.dname
from emp e
JOIN dept d
USING(deptno);
 
EMPNO ENAME   DEPTNO DNAME

----- ------- ------ ---------------
 7782 CLARK       10 ACCOUNTING
 
7839 KING        10 ACCOUNTING
 
7934 MILLER      10 ACCOUNTING
 
7566 JONES       20 RESEARCH
 
7902 FORD        20 RESEARCH
 
7876 ADAMS       20 RESEARCH
 
7369 SMITH       20 RESEARCH
 
7788 SCOTT       20 RESEARCH
 
7521 WARD        30 SALES
 
7844 TURNER      30 SALES
 
7499 ALLEN       30 SALES
 
7900 JAMES       30 SALES
 
7698 BLAKE       30 SALES
 
7654 MARTIN      30 SALES
 

14 rows selected.

用韦恩图表示INNER JOIN大概是这样的

http://pafumi.net/SQL_Joins.html

 

 

外连接:OUTER JOIN

1、全连接 FULL OUT JOIN

全连接:

包括左、右表的全部行,无论另外一边的表中是否存在与它们匹配的行。不符合条件的,均空值代替。

参照 emp 创建员工表 t1emp,参照 dept 创建部门表 t1dept,并修改测试数据

SQL> show user
USER is "SCOTT"
SQL> create table t1emp as select * from emp;
 

Table created.
 

SQL> create table t1dept as select * from dept;
 

Table created.
 

SQL> update t1emp set deptno=99 where empno in(7369,7499,7782);
 

3 rows updated.
 

SQL> select * from t1emp;
 
EMPNO ENAME   JOB          MGR HIREDATE              SAL  COMM DEPTNO

----- ------- ---------- ----- ------------------- ----- ----- ------
 7369 SMITH   CLERK       7902 1980-12-17 00:00:00   800           99
 7499 ALLEN   SALESMAN    7698 1981-02-20 00:00:00  1600   300     99
 7521 WARD    SALESMAN    7698 1981-02-22 00:00:00  1250   500     30
 7566 JONES   MANAGER     7839 1981-04-02 00:00:00  2975           20
 7654 MARTIN  SALESMAN    7698 1981-09-28 00:00:00  1250  1400     30
 7698 BLAKE   MANAGER     7839 1981-05-01 00:00:00  2850           30
 7782 CLARK   MANAGER     7839 1981-06-09 00:00:00  2450           99
 7788 SCOTT   ANALYST     7566 1987-04-19 00:00:00  3000           20
 7839 KING    PRESIDENT        1981-11-17 00:00:00  5000           10
 7844 TURNER  SALESMAN    7698 1981-09-08 00:00:00  1500     0     30
 7876 ADAMS   CLERK       7788 1987-05-23 00:00:00  1100           20
 7900 JAMES   CLERK       7698 1981-12-03 00:00:00   950           30
 7902 FORD    ANALYST     7566 1981-12-03 00:00:00  3000           20
 7934 MILLER  CLERK       7782 1982-01-23 00:00:00  1300           10
 
14 rows selected.
 

SQL> select * from t1dept;
 
DEPTNO DNAME           LOC

------ --------------- ---------------
    10 ACCOUNTING      NEW YORK
   
20 RESEARCH        DALLAS
   
30 SALES           CHICAGO
   
40 OPERATIONS      BOSTON

 

 

SQL>
select e.empno,e.ename,e.deptno,d.deptno,d.dname
from t1emp e
FULL OUTER JOIN t1dept d
ON e.deptno = d.deptno;
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7369 SMITH       99    
 7499 ALLEN       99    
 7521 WARD        30     30 SALES
 
7566 JONES       20     20 RESEARCH
 
7654 MARTIN      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7782 CLARK       99    

 7788 SCOTT       20     20 RESEARCH
 
7839 KING        10     10 ACCOUNTING
 
7844 TURNER      30     30 SALES
 
7876 ADAMS       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7902 FORD        20     20 RESEARCH
 
7934 MILLER      10     10 ACCOUNTING
                        
40 OPERATIONS
 

15 rows selected.

用韦恩图表示INNER JOIN大概是这样的

 

2、左外连接LEFT OUT JOIN

左外连接:

又叫左连接,包含左边表所有记录,右边所有的匹配的记录,没有则用NULL补齐。也就是说列出左边表全部的,及右边表符合条件的,不符合条件的以空值代替

SQL>

select e.empno,e.ename,e.deptno,d.deptno,d.dname
   
from t1emp e
   
LEFT OUTER JOIN t1dept d
   
ON e.deptno = d.deptno;
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7934 MILLER      10     10 ACCOUNTING
 
7839 KING        10     10 ACCOUNTING
 
7902 FORD        20     20 RESEARCH
 
7876 ADAMS       20     20 RESEARCH
 
7788 SCOTT       20     20 RESEARCH
 
7566 JONES       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7844 TURNER      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7654 MARTIN      30     30 SALES
 
7521 WARD        30     30 SALES
 
7782 CLARK       99
 7499 ALLEN       99
 7369 SMITH       99
 
14 rows selected.
 

SQL>

select e.empno,e.ename,e.deptno,d.deptno,d.dname
   
from t1emp e
   
LEFT JOIN t1dept d
   
ON e.deptno = d.deptno;
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7934 MILLER      10     10 ACCOUNTING
 
7839 KING        10     10 ACCOUNTING
 
7902 FORD        20     20 RESEARCH
 
7876 ADAMS       20     20 RESEARCH
 
7788 SCOTT       20     20 RESEARCH
 
7566 JONES       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7844 TURNER      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7654 MARTIN      30     30 SALES
 
7521 WARD        30     30 SALES
 
7782 CLARK       99
 7499 ALLEN       99
 7369 SMITH       99
 
14 rows selected.

在ORACLE 9i之前,使用在(+)来表示左连接,哪个带(+)哪个需要条件符合的,另一个全部的。即放左表示右连接,放右表示左连接

SQL> select e.empno,e.ename,e.deptno,d.deptno,d.dname from t1emp e,t1dept d where e.deptno=d.deptno(+);
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7934 MILLER      10     10 ACCOUNTING
 
7839 KING        10     10 ACCOUNTING
 
7902 FORD        20     20 RESEARCH
 
7876 ADAMS       20     20 RESEARCH
 
7788 SCOTT       20     20 RESEARCH
 
7566 JONES       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7844 TURNER      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7654 MARTIN      30     30 SALES
 
7521 WARD        30     30 SALES
 
7782 CLARK       99
 7499 ALLEN       99
 7369 SMITH       99
 
14 rows selected.
 

SQL> select e.empno,e.ename,e.deptno,d.deptno,d.dname from t1emp e,t1dept d where d.deptno(+)=e.deptno;
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7934 MILLER      10     10 ACCOUNTING
 
7839 KING        10     10 ACCOUNTING
 
7902 FORD        20     20 RESEARCH
 
7876 ADAMS       20     20 RESEARCH
 
7788 SCOTT       20     20 RESEARCH
 
7566 JONES       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7844 TURNER      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7654 MARTIN      30     30 SALES
 
7521 WARD        30     30 SALES
 
7782 CLARK       99
 7499 ALLEN       99
 7369 SMITH       99
 
14 rows selected.

结论:1.加号(+)在右表,就属于左连接,以左表数据为基准(:左右表的区分要看在 from 中的位 置); 2.由于(+)的位置容易混淆连接的方式,所以工作中在 oracle 的环境中多采用

sql-99 的书写方式 (left [outer] join),便于识别连接方式

 

用韦恩图表示INNER JOIN大概是这样的

 

 

3、右外连接:RIGHT JOIN

右外连接

又叫右连接: 包含右边表所有记录,左边所有的匹配的记录,没有则用NULL补齐,也就是说,与左连接一样,列出右边表全部的,及左边表符合条件的,不符合条件的用空值代替

SQL>

select e.empno,e.ename,e.deptno,d.deptno,d.dname

from t1emp e

RIGHT OUTER JOIN t1dept d

ON e.deptno=d.deptno;              
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7521 WARD        30     30 SALES
 
7566 JONES       20     20 RESEARCH
 
7654 MARTIN      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7788 SCOTT       20     20 RESEARCH
 
7839 KING        10     10 ACCOUNTING
 
7844 TURNER      30     30 SALES
 
7876 ADAMS       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7902 FORD        20     20 RESEARCH
 
7934 MILLER      10     10 ACCOUNTING
                        
40 OPERATIONS
 

12 rows selected.
 

SQL>

select e.empno,e.ename,e.deptno,d.deptno,d.dname

from t1emp e

RIGHT JOIN t1dept d

ON e.deptno=d.deptno;
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7521 WARD        30     30 SALES
 
7566 JONES       20     20 RESEARCH
 
7654 MARTIN      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7788 SCOTT       20     20 RESEARCH
 
7839 KING        10     10 ACCOUNTING
 
7844 TURNER      30     30 SALES
 
7876 ADAMS       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7902 FORD        20     20 RESEARCH
 
7934 MILLER      10     10 ACCOUNTING
                        
40 OPERATIONS
 

12 rows selected.
 

SQL>

select e.empno,e.ename,e.deptno,d.deptno,d.dname

from t1emp e,t1dept d

where e.deptno(+)=d.deptno;
 
EMPNO ENAME   DEPTNO DEPTNO DNAME

----- ------- ------ ------ ---------------
 7521 WARD        30     30 SALES
 
7566 JONES       20     20 RESEARCH
 
7654 MARTIN      30     30 SALES
 
7698 BLAKE       30     30 SALES
 
7788 SCOTT       20     20 RESEARCH
 
7839 KING        10     10 ACCOUNTING
 
7844 TURNER      30     30 SALES
 
7876 ADAMS       20     20 RESEARCH
 
7900 JAMES       30     30 SALES
 
7902 FORD        20     20 RESEARCH
 
7934 MILLER      10     10 ACCOUNTING
                        
40 OPERATIONS
 

12 rows selected.

用韦恩图表示INNER JOIN大概是这样的

 

笛卡尔积:CROSS JOIN

CROSS JOIN就是笛卡尔乘积连接,不需要任何关联条件,实现M*N的结果集,其实这种SQL JOIN方式基本上只在理论上有意义,实际当中,很少有用的CORSS JOIN方式。

注意: cross join跟inner join、outer join等有所不同,不需要关键词on,因为它不需要相关字段做关联。

注意:此时得到的结果中,有些数据是不正常的,成为业务中的‘垃圾数据’(没有实际的意义);但是, 有时候需要短时间内生成大量的测试数据,便于进行压力测试,或者性能测试,此时笛卡尔积才有点意义;

 

建立测试表tcs1、tcs2并插入数据

SQL> create table tcs1(id number(2),name varchar2(10),deptno number(2));
 

Table created.
 

SQL> create table tcs2(deptno number(2),dname varchar2(10),loc varchar2(10));
 

Table created.
 

SQL> insert into tcs1 values(1,'name1',10);
 

1 row created.
 

SQL> insert into tcs1 values(2,'name2',20);
 

1 row created.
 

SQL> insert into tcs1 values(3,'name3',30);
 

1 row created.
 

SQL> insert into tcs2 values(10,'dept1','loc1');
 

1 row created.
 

SQL> insert into tcs2 values(20,'dept2','loc2');
 

1 row created.
 

SQL> insert into tcs2 values(30,'dept3','loc3');
 

1 row created.
 

SQL> commit;
 

Commit complete.
 

SQL> select * from tcs1;
 
       
ID NAME                           DEPTNO
---------- ------------------------------ ------
         1 name1                              10
         2 name2                              20
         3 name3                              30
 
SQL> select * from tcs2;
 
DEPTNO DNAME           LOC

------ --------------- ---------------
    10 dept1           loc1
   
20 dept2           loc2
   
30 dept3           loc3

查看笛卡尔积CROSS JOIN

SQL> select rownum,tcs1.name,tcs2.dname from tcs1 CROSS JOIN tcs2;
 
    ROWNUM
NAME                           DNAME
---------- ------------------------------ ---------------
         1 name1                          dept1
        
2 name1                          dept2
        
3 name1                          dept3
        
4 name2                          dept1
        
5 name2                          dept2
        
6 name2                          dept3
        
7 name3                          dept1
        
8 name3                          dept2
        
9 name3                          dept3
 

9 rows selected.
 

SQL> select rownum,tcs1.name,tcs2.dname from tcs1,tcs2;
 
    ROWNUM
NAME                           DNAME
---------- ------------------------------ ---------------
         1 name1                          dept1
        
2 name1                          dept2
        
3 name1                          dept3
        
4 name2                          dept1
        
5 name2                          dept2
        
6 name2                          dept3
        
7 name3                          dept1
        
8 name3                          dept2
        
9 name3                          dept3
 

9 rows selected.

本来应该是可以确定每个人在哪个部门,但是因为笛卡尔积的原因,出现了不正常数据

笛卡尔积无法用韦恩图表示

 

自然连接:NATURAL JOIN

在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选择列表指出查询结果集合中所包括的列,并删除连接表中的重复列

创建测试表tna1、tna2,并插入数据

SQL> create table tna1(id number(2),name varchar2(10));
 

Table created.
 

SQL> create table tna2(id number(2),other varchar2(10));
 

Table created.
 

SQL> insert into tna1 values(1,'name1');
 

1 row created.
 

SQL> insert into tna1 values(1,'name1');
 

1 row created.
 

SQL> insert into tna1 values(2,'name2');
 

1 row created.
 

SQL> insert into tna2 values(1,'other1');
 

1 row created.
 

SQL> insert into tna2 values(2,'other2');
 

1 row created.
 

SQL> insert into tna2 values(3,'other3');
 

1 row created.
 

SQL> select * from tna1;
 
       
ID NAME
---------- ------------------------------
         1 name1
        
1 name1
        
2 name2
 

SQL> select * from tna2;
 
       
ID OTHER
---------- ------------------------------
         1 other1
        
2 other2
        
3 other3
 

SQL> select * from tna1

NATURAL JOIN tna2;
 
       
ID NAME                           OTHER
---------- ------------------------------ --------------
         1 name1                          other1
        
1 name1                          other1
        
2 name2                          other2

官方解释:

The NATURAL keyword indicates that a natural join is being performed. A natural join is based on all columns in the two tables that have the same name. It selects rows from the two tables that have equal values in the relevant columns. When specifying columns that are involved in the natural join, do not qualify the column name with a table name or table alias

 

另外NATURAL JOIN 仅需两张表中字段名称相同,即可完成连接

举例如下

建立表tna3、tna4并插入数据

SQLcreate table tna3(id number(2),name varchar2(10));
 

Table created.
 

SQL> create table tna4(id varchar2(2),other varchar2(10));
 

Table created.
 

SQL> insert into tna3  values(1,’name1’);
 

1 row created.
 

SQL> insert into tna3  values(1,’name1’);
 

1 row created.
 

SQL> insert into tna3  values(2,’name2’);
 

1 row created.
 

SQL> insert into tna4 values('1','other1');
 

1 row created.
 

SQL> insert into tna4 values('2','other2');
 

1 row created.
 

SQL> insert into tna4 values('3','other3');
 

1 row created.
 

SQL> select * from tna3;
 
       
ID NAME
---------- ------------------------------
         1 name1
        
1 name1
        
2 name2
 

SQL> select * from tna4;
 

ID     OTHER
------ ------------------------------
1      other1
2      other2
3      other3
 
 

SQL> desc tna3;
 
Name              Null?    Type
 ----------------- -------- ------------
 ID                         NUMBER(2)
 
NAME                       VARCHAR2(10)
 

SQL> desc tna4;
 
Name              Null?    Type
 ----------------- -------- ------------
 ID                         VARCHAR2(2)
 OTHER                     
VARCHAR2(10)
 

SQL> select * from tna3 NATURAL JOIN tna4;
 

ID     NAME                  OTHER
------ --------------------- ------------------------------
1      name1                 other1
1      name1                 other1
2      name2                 other2

自然连接的两个表的有多个字段都满足有相同名称,那么他们会被作为自然连接的条件

SQL> create table tna5(id number(2),name varchar2(10));
 

Table created.
 

SQL> create table tna6(id number(2),name varchar2(10));
 

Table created.
 

SQL> insert into tna5 values(1,'name1');                 
 

1 row created.
 

SQL> insert into tna5 values(2,'name2');
 

1 row created.
 

SQL> insert into tna6 values(1,'name3');
 

1 row created.
 

SQL> insert into tna6 values(2,'name2');
 

1 row created.
 

SQL> select * from tna5;
 
       
ID NAME
---------- ------------------------------
         1 name1
        
2 name2
 

SQL> select * from tna6;
 
       
ID NAME
---------- ------------------------------
         1 name3
        
2 name2
 

SQL> select * from tna5 NATURAL JOIN tna6;
 
       
ID NAME
---------- ------------------------------
         2 name2

自然连接的韦恩图和内连接的一样

 

SEMI JOIN

通常出现在使用了exists或in的sql中,所谓semi-join即在两表关联时,当第二个表中存在一个或多个匹配记录时,返回第一个表的记录;

与普通join的区别在于semi-join时,第一个表里的记录最多只返回一次

 

In the following example, only one row needs to be returned from the departments table, even though many rows in the employees table might match the subquery. If no index has been defined on thesalary column in employees, then a semijoin can be used to improve query performance.

 

SELECT * FROM departments

   WHERE EXISTS

   (SELECT * FROM employees

       WHERE departments.department_id = employees.department_id

       AND employees.salary > 2500)

   ORDER BY department_name;

举例如下

创建部门测试表tsemi1和测试员工表tsemi2并修改数据

SQL> show user;
USER is "SCOTT"
SQL> create table tsemi1 as select * from dept;
 

Table created.
 

SQL> create table tsemi2 as select * from emp;
 

Table created.
 

SQL> select * from tsemi1;
 
DEPTNO DNAME           LOC

------ --------------- ---------------
    10 ACCOUNTING      NEW YORK
   
20 RESEARCH        DALLAS
   
30 SALES           CHICAGO
   
40 OPERATIONS      BOSTON
 

SQL> select * from tsemi2;
 
EMPNO ENAME   JOB          MGR HIREDATE              SAL  COMM DEPTNO

----- ------- ---------- ----- ------------------- ----- ----- ------
 7369 SMITH   CLERK       7902 1980-12-17 00:00:00   800           20
 7499 ALLEN   SALESMAN    7698 1981-02-20 00:00:00  1600   300     30
 7521 WARD    SALESMAN    7698 1981-02-22 00:00:00  1250   500     30
 7566 JONES   MANAGER     7839 1981-04-02 00:00:00  2975           20
 7654 MARTIN  SALESMAN    7698 1981-09-28 00:00:00  1250  1400     30
 7698 BLAKE   MANAGER     7839 1981-05-01 00:00:00  2850           30
 7782 CLARK   MANAGER     7839 1981-06-09 00:00:00  2450           10
 7788 SCOTT   ANALYST     7566 1987-04-19 00:00:00  3000           20
 7839 KING    PRESIDENT        1981-11-17 00:00:00  5000           10
 7844 TURNER  SALESMAN    7698 1981-09-08 00:00:00  1500     0     30
 7876 ADAMS   CLERK       7788 1987-05-23 00:00:00  1100           20
 7900 JAMES   CLERK       7698 1981-12-03 00:00:00   950           30
 7902 FORD    ANALYST     7566 1981-12-03 00:00:00  3000           20
 7934 MILLER  CLERK       7782 1982-01-23 00:00:00  1300           10
 
15 rows selected.

查询出职员不为空的部门

用普通连接查询会产生重复记录(打开执行计划)

SQL> alter session set statistics_level=all;
 

Session altered.
 

SQL>

select d.deptno,d.dname

from tsemi1 d,tsemi2 e

where e.deptno=d.deptno;
 
DEPTNO DNAME

------ ---------------
    20 RESEARCH
   
30 SALES
   
30 SALES
   
20 RESEARCH
   
30 SALES
   
30 SALES
   
10 ACCOUNTING
   
20 RESEARCH
   
10 ACCOUNTING
   
30 SALES
   
20 RESEARCH
   
30 SALES
   
20 RESEARCH
   
10 ACCOUNTING
 

14 rows selected.
 
 
Execution Plan

----------------------------------------------------------
Plan hash value: 674227419
 
---------------------------------------------------------------------
| Id | Operation          |Name  |Rows| Bytes |Cost (%CPU)|Time    
---------------------------------------------------------------------
0 | SELECT STATEMENT   |      | 15 |   525 |    7   (0)|00:00:01 |
|*
1 |  HASH JOIN         |      | 15 |   525 |    7   (0)|00:00:01 |
2 |   TABLE ACCESS FULL|TSEMI1|  4 |    88 |    3   (0)|00:00:01 |
3 |   TABLE ACCESS FULL|TSEMI2| 15 |   195 |    4   (0)|00:00:01 |
---------------------------------------------------------------------
 
Predicate Information (identified
by operation id):
---------------------------------------------------
 
  
1 - access("E"."DEPTNO"="D"."DEPTNO")
 
Note

-----
   - dynamic sampling used for this statement (level=2)

而使用semi-join时候,不会返回重复记录

SQL>

select d.deptno,d.dname

from tsemi1 d

where d.deptno

in(

select e.deptno

from tsemi2 e

             );
 
DEPTNO DNAME

------ ---------------
    20 RESEARCH
   
30 SALES
   
10 ACCOUNTING
 
 
Execution Plan

----------------------------------------------------------
Plan hash value: 2980023518
 
---------------------------------------------------------------------
|Id | Operation          |Name  |Rows|Bytes| Cost (%CPU)| Time     |
---------------------------------------------------------------------
0| SELECT STATEMENT   |      |  4140|     7   (0)| 00:00:01 |
|*
1|  HASH JOIN SEMI    |      |  4140|     7   (0)| 00:00:01 |
2|   TABLE ACCESS FULL|TSEMI1|  4 |   88|     3   (0)| 00:00:01 |
3|   TABLE ACCESS FULL|TSEMI2| 15195|     4   (0)| 00:00:01 |
---------------------------------------------------------------------
 
Predicate Information (identified
by operation id):
---------------------------------------------------
 
  
1 - access("D"."DEPTNO"="E"."DEPTNO")
 
Note

-----
   - dynamic sampling used for this statement (level=2)

 

ANTI JOIN

 

anti-join则与semi-join相反,即当在第二张表没有发现匹配记录时,才会返回第一张表里的记录;

当使用not exists/not in的时候会用到,两者在处理null值的时候会有所区别

 

何时选择anti-join

1 使用not in且相应列有not null约束

2 not exists,不保证每次都用到anti-join

当无法选择anti-join时,oracle常会采用filter替代

Using Antijoins: Example

 

The following example selects a list of employees who are not in a particular set of departments:

 

SELECT * FROM employees

   WHERE department_id NOT IN

   (SELECT department_id FROM departments

       WHERE location_id = 1700)

   ORDER BY last_name;

 

举例如下

查询职员为空的部门

 

先用普通连接查询

SQL>

select d.deptno,d.dname

from tsemi1 d minus

          select d.deptno,d.dname

          from tsemi1 d

          where d.deptno

          in (

                  select e.deptno from tsemi2 e

                 );
 
DEPTNO DNAME

------ ---------------
    40 OPERATIONS
 
 
Execution Plan

----------------------------------------------------------
Plan hash value: 2826136730
 
----------------------------------------------------------------------
|Id |Operation            |Name  |Rows |Bytes| Cost (%CPU)| Time     |
----------------------------------------------------------------------
0|SELECT STATEMENT     |      |   4 | 613 |    12  (17)| 00:00:01 |
|
1 | MINUS               |      |     |     |            |          |
|
2 |  SORT UNIQUE        |       |   488 |     4  (25)| 00:00:01 |
|
3 |   TABLE ACCESS FULL |TSEMI1|   488 |     3   (0)| 00:00:01 |
|
4 |  SORT UNIQUE        |      |  15 | 525 |     8  (13)| 00:00:01 |
|*
5 |   HASH JOIN         |      |  15 | 525 |     7   (0)| 00:00:01 |
|
6 |    TABLE ACCESS FULL|TSEMI1|   488 |     3   (0)| 00:00:01 |
|
7 |    TABLE ACCESS FULL|TSEMI2|  15 | 195 |     4   (0)| 00:00:01 |
----------------------------------------------------------------------
 
Predicate Information (identified
by operation id):
---------------------------------------------------
 
  
5 - access("D"."DEPTNO"="E"."DEPTNO")
 
Note

-----
   - dynamic sampling used for this statement (level=2)

使用anti join

SQL>

select d.deptno,d.dname

from tsemi1 d

where d.deptno

not in (

               select deptno from tsemi2

               );
 
DEPTNO DNAME

------ ---------------
    40 OPERATIONS
 
 
Execution Plan

----------------------------------------------------------
Plan hash value: 2185638631
 
----------------------------------------------------------------------
|Id | Operation          |Name  |Rows |Bytes| Cost (%CPU)| Time     |
----------------------------------------------------------------------
0| SELECT STATEMENT   |      |    4 | 140 |     7   (0)| 00:00:01 |
|*
1|  HASH JOIN ANTI NA |      |    4 | 140 |     7   (0)| 00:00:01 |
2|   TABLE ACCESS FULL|TSEMI1|    488 |     3   (0)| 00:00:01 |
3|   TABLE ACCESS FULL|TSEMI2|   15 | 195 |     4   (0)| 00:00:01 |
----------------------------------------------------------------------
 
Predicate Information (identified
by operation id):
---------------------------------------------------
 
  
1 - access("D"."DEPTNO"="DEPTNO")
 
Note

-----
   - dynamic sampling used for this statement (level=2)

注:倘若subquery返回一条null值,则整个not in都变为false,即不返回任何值

If the subquery of a NOT IN clause returns at least one row with a null value, the entire NOT IN clause evaluates to false for all rows

插入一条空值

SQL> insert into tsemi2 values(9999,null,null,null,sysdate,null,null,null);
 

1 row created.

再次使用anti join没有返回数据

SQL> select d.deptno,d.dname from tsemi1 d where d.deptno not in (select deptno from tsemi2);
 

no rows selected
 
 
Execution Plan

----------------------------------------------------------
Plan hash value: 2185638631
 
----------------------------------------------------------------------
|Id | Operation          | Name   |Rows|Bytes| Cost (%CPU)| Time     |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT   |        |  4 | 140 |     7   (0)| 00:00:01 |
|*
1 |  HASH JOIN ANTI NA |        |  4 | 140 |     7   (0)| 00:00:01 |
|
2 |   TABLE ACCESS FULL| TSEMI1 |  488 |     3   (0)| 00:00:01 |
|
3 |   TABLE ACCESS FULL| TSEMI2 | 15 | 195 |     4   (0)| 00:00:01 |
----------------------------------------------------------------------
 
Predicate Information (identified
by operation id):
---------------------------------------------------
 
  
1 - access("D"."DEPTNO"="DEPTNO")
 
Note

-----
   - dynamic sampling used for this statement (level=2)

 

自连接:SELF JOIN

从表自身内部的关联中查询相关信息

例:想知道 emp 表中,每位员工对应的上级主管的名字

SQL> select e.empno,e.ename,e.mgr,m.empno,m.ename from emp e,emp m where e.mgr=m.empno;
 
EMPNO ENAME     MGR EMPNO ENAME

----- ------- ----- ----- -------
 7902 FORD     7566  7566 JONES
 
7788 SCOTT    7566  7566 JONES
 
7844 TURNER   7698  7698 BLAKE
 
7499 ALLEN    7698  7698 BLAKE
 
7521 WARD     7698  7698 BLAKE
 
7900 JAMES    7698  7698 BLAKE
 
7654 MARTIN   7698  7698 BLAKE
 
7934 MILLER   7782  7782 CLARK
 
7876 ADAMS    7788  7788 SCOTT
 
7698 BLAKE    7839  7839 KING
 
7566 JONES    7839  7839 KING
 
7782 CLARK    7839  7839 KING
 
7369 SMITH    7902  7902 FORD
 

13 rows selected.

参考资料https://blog.csdn.net/fhy36897/article/details/89308250

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aluphami

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值