目录
前置说明:本文主要oracle 9i的经典雇员信息测试表为例,进行示例演示。
该表有三个子表构成,分别为:(1). dept -- 部门信息表、(2). emp -- 雇员信息表 、(3). salgrade -- 薪资等级表,下面是每张表的desc字段及属性信息:
mysql> desc dept;
+--------+--------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------------------+------+-----+---------+-------+
| deptno | int(2) unsigned zerofill | NO | | NULL | |
| dname | varchar(14) | YES | | NULL | |
| loc | varchar(13) | YES | | NULL | |
+--------+--------------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
mysql> desc emp;
+----------+--------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------------------+------+-----+---------+-------+
| empno | int(6) unsigned zerofill | NO | | NULL | |
| ename | varchar(10) | YES | | NULL | |
| job | varchar(9) | YES | | NULL | |
| mgr | int(4) unsigned zerofill | YES | | NULL | |
| hiredate | datetime | YES | | NULL | |
| sal | decimal(7,2) | YES | | NULL | |
| comm | decimal(7,2) | YES | | NULL | |
| deptno | int(2) unsigned zerofill | YES | | NULL | |
+----------+--------------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
mysql> desc salgrade;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| grade | int(11) | YES | | NULL | |
| losal | int(11) | YES | | NULL | |
| hisal | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)
多表查询,就是在由多张表组合而成的大表中,检索符合条件的数据。
语法:select ... from 表1,表2,... [where ...];
多张表进行组合,就是在对表进行笛卡尔积,图1.1为双表笛卡尔积运算的规则。由于实际项目中多张表之间可能会存在相互关联的字段,要对多表组合结果进行筛选去除非法组合,比如:对emp和dept表进行笛卡尔积组合运算时,emp表中的deptno字段和dept表中的deptno字段相同才有意义,因此要通过适当的where条件,将无效的组合去除。
MySQL还支持表的自连接,所谓自连接,就是同一张表连接其自身,图1.2为自连接的示意图,可以理解为两张完全相同的表做笛卡尔积。
部门名在表dept中,员工姓名和员工编号在表emp中,emp中还存有员工所属的部门编号,因此需要将表emp和dept进行组合,并通过emp.deptno=dept.deptno排除不正确的组合项。
mysql> select empno,ename,emp.deptno from emp,dept where emp.deptno=dept.deptno and emp.deptno=20;
+--------+-------+--------+
| empno | ename | deptno |
+--------+-------+--------+
| 007369 | SMITH | 20 |
| 007566 | JONES | 20 |
| 007788 | SCOTT | 20 |
| 007876 | ADAMS | 20 |
| 007902 | FORD | 20 |
+--------+-------+--------+
5 rows in set (0.00 sec)
员工姓名和薪资在表emp中,薪资等级信息在表salgrade中,需要将表emp和salgrade进行组合查询。在salgrade表中存有每种薪资等级的最高薪资hisal和最低薪资losal,筛选满足条件losal <= sal <= hisal的行即可。
mysql> select ename,sal,grade from emp,salgrade -- 组合表
-> where sal>=losal and sal<=hisal; -- 筛选条件
+--------+---------+-------+
| ename | sal | grade |
+--------+---------+-------+
| SMITH | 800.00 | 1 |
| ALLEN | 1600.00 | 3 |
| WARD | 1250.00 | 2 |
| JONES | 2975.00 | 4 |
| MARTIN | 1250.00 | 2 |
| BLAKE | 2850.00 | 4 |
| CLARK | 2450.00 | 4 |
| SCOTT | 3000.00 | 4 |
| KING | 5000.00 | 5 |
| TURNER | 1500.00 | 3 |
| ADAMS | 1100.00 | 1 |
| JAMES | 950.00 | 1 |
| FORD | 3000.00 | 4 |
| MILLER | 1300.00 | 2 |
+--------+---------+-------+
14 rows in set (0.00 sec)
子查询,就是在where条件中,嵌入select语句来作为判断条件的一部分。
单行子查询,就是通过select筛选出来的条件为单行数据,通过where将特定字段的值与select结果进行比较,得到满足要求的结果。
语法:select ... from TableName where [... (select ...)];
在where条件中通过select子查询获取CLARK的薪资,筛选出sal大于CLARK薪资的行即可。
mysql> select ename,sal from emp where sal > (select sal from emp where ename='CLARK');
+-------+---------+
| ename | sal |
+-------+---------+
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| FORD | 3000.00 |
+-------+---------+
5 rows in set (0.00 sec)
mysql> select empno,ename,deptno from emp
-> where deptno = (select deptno from emp where ename='SMITH');
+--------+-------+--------+
| empno | ename | deptno |
+--------+-------+--------+
| 007369 | SMITH | 20 |
| 007566 | JONES | 20 |
| 007788 | SCOTT | 20 |
| 007876 | ADAMS | 20 |
| 007902 | FORD | 20 |
+--------+-------+--------+
5 rows in set (0.00 sec
首先介绍in、all、any三个关键字:
下面三个案例,分别使用in、all、any进行多行子查询。
案例1:查询与10号部门工作岗位相同的员工工号、姓名、部门和岗位,但不包含10号部门员工
通过 in关键字 + select 子查询,将每个员工的job与10号部门具有的岗位进行比较,同时排除10号部门员工即可。
mysql> select empno,ename,deptno,job from emp where job in(select job from emp where deptno=10) and deptno!=10;
+--------+-------+--------+---------+
| empno | ename | deptno | job |
+--------+-------+--------+---------+
| 007566 | JONES | 20 | MANAGER |
| 007698 | BLAKE | 30 | MANAGER |
| 007369 | SMITH | 20 | CLERK |
| 007876 | ADAMS | 20 | CLERK |
| 007900 | JAMES | 30 | CLERK |
+--------+-------+--------+---------+
5 rows in set (0.00 sec)
案例2:查询比30号部门所有员工工资都高的员工姓名、部门和工资
mysql> select ename,deptno,sal from emp where sal > all(select sal from emp where deptno=30);
+-------+--------+---------+
| ename | deptno | sal |
+-------+--------+---------+
| JONES | 20 | 2975.00 |
| SCOTT | 20 | 3000.00 |
| KING | 10 | 5000.00 |
| FORD | 20 | 3000.00 |
+-------+--------+---------+
4 rows in set (0.00 sec)
案例三:查询比30号部门任意员工工资高的员工姓名、部门号和薪资,不包括30号部门员工
mysql> select ename,deptno,sal from emp where sal > any(select sal from emp where deptno=30);
+--------+--------+---------+
| ename | deptno | sal |
+--------+--------+---------+
| ALLEN | 30 | 1600.00 |
| WARD | 30 | 1250.00 |
| JONES | 20 | 2975.00 |
| MARTIN | 30 | 1250.00 |
| BLAKE | 30 | 2850.00 |
| CLARK | 10 | 2450.00 |
| SCOTT | 20 | 3000.00 |
| KING | 10 | 5000.00 |
| TURNER | 30 | 1500.00 |
| ADAMS | 20 | 1100.00 |
| FORD | 20 | 3000.00 |
| MILLER | 10 | 1300.00 |
+--------+--------+---------+
12 rows in set (0.00 sec)
多列子查询,就是多个字段与select后的值进行比较,将要进行比较的字段使用圆括号括起来,每个字段之间通过逗号隔开。
语法:
select ...? from Table where (field1, filed2, ...) 运算符 (select field1, filed2, ... from Table)
mysql> select empno,ename,deptno,job from emp
-> where (job,deptno) = (select job,deptno from emp where ename='SMITH')
-> and ename != 'SMITH';
+--------+-------+--------+-------+
| empno | ename | deptno | job |
+--------+-------+--------+-------+
| 007876 | ADAMS | 20 | CLERK |
+--------+-------+--------+-------+
1 row in set (0.00 sec)
在from语句中可以使用select子查询,方式一般为将select后的结果作为一张表,并给一个临时的名称,这张表一般会和其他的表进行组合,在组合后的表中进行查询。通过案例来演示如何在from中使用子查询。
在表emp中通过group by对部门分组,结合avg(sal)计算每个部门的平均工资,通过select生成一张包含部门号和部门平均工资的临时表tmp,并将avg(sal)重命名为avg_sal,将tmp与emp表组合,通过tmp.deptno=emp.deptno去除错误的组合,通过sal>avg_sal筛选大于部门平均工资的员工。
mysql> select ename,emp.deptno,sal,avg_sal from emp,
-> (select avg(sal) avg_sal,deptno from emp group by deptno) tmp
-> where emp.deptno=tmp.deptno and sal>avg_sal;
+-------+--------+---------+-------------+
| ename | deptno | sal | avg_sal |
+-------+--------+---------+-------------+
| KING | 10 | 5000.00 | 2916.666667 |
| JONES | 20 | 2975.00 | 2175.000000 |
| SCOTT | 20 | 3000.00 | 2175.000000 |
| FORD | 20 | 3000.00 | 2175.000000 |
| ALLEN | 30 | 1600.00 | 1566.666667 |
| BLAKE | 30 | 2850.00 | 1566.666667 |
+-------+--------+---------+-------------+
6 rows in set (0.00 sec)
创建一张包含部门号deptno和部门最低工资min(sal)的临时表tmp,将tmp和emp进行组合,筛选符合条件的数据。
mysql> select ename,emp.deptno,sal from emp,
-> (select min(sal) minSal,deptno from emp group by deptno) tmp
-> where sal=minSal and emp.deptno=tmp.deptno;
+--------+--------+---------+
| ename | deptno | sal |
+--------+--------+---------+
| SMITH | 20 | 800.00 |
| JAMES | 30 | 950.00 |
| MILLER | 10 | 1300.00 |
+--------+--------+---------+
3 rows in set (0.00 sec)
通过select筛选在emp中根据deptno分组统计人员数量,获取一张包括部门编号和部门人员数量的表tmp,将这张表tmp与表dept组合,筛选符合条件的数据即可。
mysql> select dept.deptno,dept.dname,dept.loc,tmp.countEmp from dept,
-> (select deptno,count(*) countEmp from emp group by deptno) tmp
-> where dept.deptno=tmp.deptno;
+--------+------------+----------+----------+
| deptno | dname | loc | countEmp |
+--------+------------+----------+----------+
| 10 | ACCOUNTING | NEW YORK | 3 |
| 20 | RESEARCH | DALLAS | 5 |
| 30 | SALES | CHICAGO | 6 |
+--------+------------+----------+----------+
3 rows in set (0.00 sec)
union用于合并多个select语句筛选出来的数据(行),合并后的结果会自动去重。
mysql> select ename,job,sal from emp where job='MANAGER' union
-> select ename,job,sal from emp where sal>2500;
+-------+-----------+---------+
| ename | job | sal |
+-------+-----------+---------+
| JONES | MANAGER | 2975.00 |
| BLAKE | MANAGER | 2850.00 |
| CLARK | MANAGER | 2450.00 |
| SCOTT | ANALYST | 3000.00 |
| KING | PRESIDENT | 5000.00 |
| FORD | ANALYST | 3000.00 |
+-------+-----------+---------+
6 rows in set (0.00 sec)
union all与union类似,可以用于合并两条select语句的执行结果,与union不同的是,union all合并后的结果不会自动去重。使用union all筛选职位是MANAGER或薪资大于2500的员工,可见执行结果明显没有去重。
mysql> select ename,job,sal from emp where job='MANAGER' union all
-> select ename,job,sal from emp where sal>2500;
+-------+-----------+---------+
| ename | job | sal |
+-------+-----------+---------+
| JONES | MANAGER | 2975.00 |
| BLAKE | MANAGER | 2850.00 |
| CLARK | MANAGER | 2450.00 |
| JONES | MANAGER | 2975.00 |
| BLAKE | MANAGER | 2850.00 |
| SCOTT | ANALYST | 3000.00 |
| KING | PRESIDENT | 5000.00 |
| FORD | ANALYST | 3000.00 |
+-------+-----------+---------+
8 rows in set (0.00 sec)