一、用到的三个表
员工表:
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
部门表:
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
薪资等级表:
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
二、子查询
嵌套在其他查查询中的查询叫做子查询。
问题描述:
假设现在我们要查询所有在 NEW YORK 和 CHICAGO工作的员工,但是
员工的工作地址信息存储在部门表中,员工具体姓名存在员工表中。
1、先查找 NEW YORK 和 CHICAGO工作的员工的部门编号
mysql> select deptno from dept where loc in ('NEW YORK','CHICAGO');
+--------+
| deptno |
+--------+
| 10 |
| 30 |
+--------+
2 rows in set (0.00 sec)
2、然后查询部门编号为10和30的员工
mysql> select ename ,deptno from emp where deptno in(10,30);
+--------+--------+
| ename | deptno |
+--------+--------+
| ALLEN | 30 |
| WARD | 30 |
| MARTIN | 30 |
| BLAKE | 30 |
| CLARK | 10 |
| KING | 10 |
| TURNER | 30 |
| JAMES | 30 |
| MILLER | 10 |
+--------+--------+
9 rows in set (0.00 sec)
3、使用子查询合并两个查询语句
mysql> select ename ,deptno from emp where deptno in (select deptno from dept where loc in ('NEW YORK','CHICAGO'));
+--------+--------+
| ename | deptno |
+--------+--------+
| ALLEN | 30 |
| WARD | 30 |
| MARTIN | 30 |
| BLAKE | 30 |
| CLARK | 10 |
| KING | 10 |
| TURNER | 30 |
| JAMES | 30 |
| MILLER | 10 |
+--------+--------+
9 rows in set (0.00 sec)
分析:
select 语句的执行是从内朝外的,上面的SQL语句其实执行两个操作,先部门表中找到了 new york 和 chicago 员工的部门编号,查询到的结果以IN操作符要求的逗号分隔的形式传给了外部查询的where子句,然后外部查询在正常执行,所以我们看到两种方案查询到的结果是相同的。
注意事项
- 在where子句中使用子查询,应该保证select语句中有于where子句中相同数目的列。通常子查询将会返回单个列然后于单个列进行匹配,如果有需要也可以使用多个列。
- 嵌套过多的子查询可能会严重影响查询的性能
三、格式化SQL
为了是包含子查询的SELECT语句阅读和调试更容易,把子查询做适当的分解多行和缩进可以简化子查询的使用。
示例:
mysql> select ename ,deptno
from emp
where deptno in (select deptno
from dept
where loc in ('NEW YORK','CHICAGO'));
+--------+--------+
| ename | deptno |
+--------+--------+
| ALLEN | 30 |
| WARD | 30 |
| MARTIN | 30 |
| BLAKE | 30 |
| CLARK | 10 |
| KING | 10 |
| TURNER | 30 |
| JAMES | 30 |
| MILLER | 10 |
+--------+--------+
9 rows in set (0.00 sec)
四、作为计算字段使用子查询
问题:
统计员工表中每个部门的人数。
- 先检索部门列表
- 对于每个部门,统计其在员工表中出现的次数
例如:我们统计部门号为30的人数
mysql> select count(*) from emp where deptno = 30;
+----------+
| count(*) |
+----------+
| 6 |
+----------+
1 row in set (0.00 sec)
为了将每个部门执行count(*)
计算,应该将count(*)
作为一个子查询。
mysql> select deptno,dname ,(select count(*) from emp where deptno = dept.deptno) as '人数'
from dept
order by (select count(*)
from emp
where deptno = dept.deptno) desc;
+--------+------------+--------+
| deptno | dname | 人数 |
+--------+------------+--------+
| 30 | SALES | 6 |
| 20 | RESEARCH | 5 |
| 10 | ACCOUNTING | 3 |
| 40 | OPERATIONS | 0 |
+--------+------------+--------+
4 rows in set (0.00 sec)
分析:
上面的select语句对于每个部门都返回三列,deptno,dname和人数。人数是一个可计算的字段,它是由圆括号括起来的子查询建立的,这个子查询对检索出爱的灭个部门号都执行一次,这个例子中被执行了四次,因为一共就只有四个部门,每次检索到一个部门,他都会去员工表中统计一次人数。
五、相关子查询
涉及外部查询的子查询叫做相关子查询
当列名存在多义性例如上面的emp.deptno = dept.deptno
,这时候就必须使用完全限定名(表明.列名)
六、用法总结
需要预先直到一个数据或者一堆数据作为某些查询的过滤条件或者总结字段时候我们先使用一个子查询
1、需要一个特定的值,但是这个值不能直接得到,需要进一步查询,子查询结果返回单行单列。
例如:查询所有薪资比MILLER高的员工
mysql> select ename,sal from emp where sal > all(select sal from emp where ename
= 'miller');
+--------+---------+
| ename | sal |
+--------+---------+
| ALLEN | 1600.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| TURNER | 1500.00 |
| FORD | 3000.00 |
+--------+---------+
8 rows in set (0.00 sec)
2、需要一堆数据,这堆数据都被作为过滤的条件,子查询结果返回单行多列
mysql> select ename ,deptno from emp where deptno in (select deptno from dept where loc in ('NEW YORK','CHICAGO'));
+--------+--------+
| ename | deptno |
+--------+--------+
| ALLEN | 30 |
| WARD | 30 |
| MARTIN | 30 |
| BLAKE | 30 |
| CLARK | 10 |
| KING | 10 |
| TURNER | 30 |
| JAMES | 30 |
| MILLER | 10 |
+--------+--------+
9 rows in set (0.00 sec)
3、需要多对信息(多列多行)作为过滤条件,子查询结果返回多列多行
mysql> select ename , sal , job ,deptno
from emp
where (deptno , sal ) in (select deptno , max(sal)
from emp
group by deptno);
+-------+---------+-----------+--------+
| ename | sal | job | deptno |
+-------+---------+-----------+--------+
| BLAKE | 2850.00 | MANAGER | 30 |
| SCOTT | 3000.00 | ANALYST | 20 |
| KING | 5000.00 | PRESIDENT | 10 |
| FORD | 3000.00 | ANALYST | 20 |
+-------+---------+-----------+--------+
4 rows in set (0.00 sec)