集合操作包括并(union & union all)、交(intersect)、差(minus)三种,为了合并多个select语句的结果看,可以使用这三处方法实现。其语法如下。

select 语句1

[union | union all | intersect | minus]

select 语句2


1.union和union all操作

 union用于获取两个或者多个结果集的并集,当使用该操作符时候,会自动去掉结果集中的重复行

 union all 操作符返回查询检索出的所有行,包括重复行。

2.Intersect,对两个结果集进行交集操作,不包括重复行

3.Minus,对两个结果集进行差操作,不包括重复行,返回语句1进行差之后的行


下面例子用来说明union,union all,Intersect,Minus的具体用法

1.基于scott用户下的emp表创建两张测试表

SQL> create table scott.emp_1 as select ename,deptno,sal from scott.emp where deptno=10 or deptno=20;


Table created.


SQL> create table scott.emp_2 as select ename,deptno,sal from scott.emp where deptno=20 or deptno=30;


Table created.


SQL> select * from scott.emp_1;


ENAME          DEPTNO        SAL

---------- ---------- ----------

SMITH              20        800

JONES              20       2975

CLARK              10       2450

SCOTT              20       3000

KING               10       5000

ADAMS              20       1100

FORD               20       3000

MILLER             10       1300


8 rows selected.


SQL> select * from scott.emp_2;


ENAME          DEPTNO        SAL

---------- ---------- ----------

SMITH              20        800

ALLEN              30       1600

WARD               30       1250

JONES              20       2975

MARTIN             30       1250

BLAKE              30       2850

SCOTT              20       3000

TURNER             30       1500

ADAMS              20       1100

JAMES              30        950

FORD               20       3000


11 rows selected.

2.现在两张表的20号部门重复,使用union连接查询


SQL> select * from scott.emp_1  

 2  union

 3  select * from scott.emp_2;


ENAME          DEPTNO        SAL

---------- ---------- ----------

ADAMS              20       1100

ALLEN              30       1600

BLAKE              30       2850

CLARK              10       2450

FORD               20       3000

JAMES              30        950

JONES              20       2975

KING               10       5000

MARTIN             30       1250

MILLER             10       1300

SCOTT              20       3000


ENAME          DEPTNO        SAL

---------- ---------- ----------

SMITH              20        800

TURNER             30       1500

WARD               30       1250


14 rows selected.

上面结果返回了14行,把两个表中的20号部门重复的行合并返回。


3.用union all查询,如下返回了两个表中的所有数据


SQL> select * from scott.emp_1

 2  union all

 3  select * from scott.emp_2;


ENAME          DEPTNO        SAL

---------- ---------- ----------

SMITH              20        800

JONES              20       2975

CLARK              10       2450

SCOTT              20       3000

KING               10       5000

ADAMS              20       1100

FORD               20       3000

MILLER             10       1300

SMITH              20        800

ALLEN              30       1600

WARD               30       1250


ENAME          DEPTNO        SAL

---------- ---------- ----------

JONES              20       2975

MARTIN             30       1250

BLAKE              30       2850

SCOTT              20       3000

TURNER             30       1500

ADAMS              20       1100

JAMES              30        950

FORD               20       3000


19 rows selected.

注意上面两个不同的查询返回的结果,在排序上有区别,可以看到使用union all是将2个查询语句组合按照原有的顺序返回,而union打乱了这个顺序,如果想使用unio排序,只需要使用order by处理下就可以了,我并没有研究union是使用什么样的规则排序的,这是在实验中得出了这一结论,需要注意order by 应该写在最后一个查询的后面,


4.查询销售部门(SALES),所有雇员的姓名(ENAME)和职位(JOB),以及职位是经理(MANAGER)的所有雇员的姓名和部门名称(dname)

SQL> select ename,dname,job

 2  from scott.emp e, scott.dept d

 3  where e.deptno=d.deptno and dname='SALES'

 4  union

 5  select ename,dname,job

 6  from scott.emp e, scott.dept d

 7  where e.deptno=d.deptno and job='MANAGER'

 8  order by dname;


ENAME      DNAME          JOB

---------- -------------- ---------

CLARK      ACCOUNTING     MANAGER

JONES      RESEARCH       MANAGER

ALLEN      SALES          SALESMAN

BLAKE      SALES          MANAGER

JAMES      SALES          CLERK

MARTIN     SALES          SALESMAN

TURNER     SALES          SALESMAN

WARD       SALES          SALESMAN


8 rows selected.

上面的结果BLAKE的雇员既是SLASE的员工,又是部门的MNAGER,他符合了两个查询条件,由于使用了union去掉了重复行,下面是改成union all的返回结果,union all保留了重复行因此BLAKE返回了两行

SQL> select ename,dname,job

 2  from scott.emp e, scott.dept d

 3  where e.deptno=d.deptno and dname='SALES'

 4  union all

 5  select ename,dname,job

 6  from scott.emp e, scott.dept d

 7  where e.deptno=d.deptno and job='MANAGER'

 8  order by dname;


ENAME      DNAME          JOB

---------- -------------- ---------

CLARK      ACCOUNTING     MANAGER

JONES      RESEARCH       MANAGER

ALLEN      SALES          SALESMAN

JAMES      SALES          CLERK

BLAKE      SALES          MANAGER

MARTIN     SALES          SALESMAN

TURNER     SALES          SALESMAN

WARD       SALES          SALESMAN

BLAKE      SALES          MANAGER


5.下面例子是一个我在面试的时候遇到的一个题目,显示emp表中所有雇员的工资,前3名的降序排列,其它的默认不变,这个用union all就可以实现

  分析:只需要将前三名的工资查询出来,然后把这个查询的结果当做第二个查询的条件,用union all连接起来就可以了,注意这里不能使用union因为union在执行合并时会打乱排序的顺序,这里是没有重复值的,所以必须要用union all来做处理

SQL> select ename,sal from (select ename,sal from scott.emp order by sal desc) where rownum < 4

 2  union all

 3  select ename,sal from scott.emp where sal not in(select sal from (select ename,sal from scott.emp order by sal desc) where rownum < 4 );


ENAME             SAL

---------- ----------

KING             5000

SCOTT            3000

FORD             3000

SMITH             800

ALLEN            1600

WARD             1250

JONES            2975

MARTIN           1250

BLAKE            2850

CLARK            2450

TURNER           1500


ENAME             SAL

---------- ----------

ADAMS            1100

JAMES             950

MILLER           1300


14 rows selected.


6.交(intersect)操作

SQL> select * from scott.emp_1

 2  intersect

 3  select * from scott.emp_2;


ENAME          DEPTNO        SAL

---------- ---------- ----------

ADAMS              20       1100

FORD               20       3000

JONES              20       2975

SCOTT              20       3000

SMITH              20        800

结果返回了2个两个表中共有的行


更新20号部门的雇员SMITH的工资,下面的例子返回的结果就不包含SMITH了,可以看出使用intersect操作返回将两个表中共有的字段值合并返回,

SQL> update scott.emp_2 set sal = 1000 where ename='SMITH';


1 row updated.


SQL> commit;


Commit complete.


SQL> select * from scott.emp_2

 2  intersect

 3  select * from scott.emp_1;


ENAME          DEPTNO        SAL

---------- ---------- ----------

ADAMS              20       1100

FORD               20       3000

JONES              20       2975

SCOTT              20       3000


7.差(minus)操作

开始还没有做下列查询的时候我非常确定以为会返回10号和30号部门的员工,刚好与intersect想法,结果回显不是这样,如下:

SQL> select * from scott.emp_1

 2  minus

 3  select * from scott.emp_2;


ENAME          DEPTNO        SAL

---------- ---------- ----------

CLARK              10       2450

KING               10       5000

MILLER             10       1300


只返回了第一个表中10号部门的员工,于是我就调换了两个表的顺序,返回结果让我一下就明白了

SQL> select * from scott.emp_2

 2  minus

 3  select * from scott.emp_1;


ENAME          DEPTNO        SAL

---------- ---------- ----------

ALLEN              30       1600

BLAKE              30       2850

JAMES              30        950

MARTIN             30       1250

SMITH              20       1000

TURNER             30       1500

WARD               30       1250


7 rows selected.


可以看出minus是将第一个表和第二个表相同的值去掉然后返回第一个表的值,如果连接了3个表,也是这一原理只会返回第一个表中在其它任意表中没有的数据