子查询又称为嵌套查询,它是指在一个select语句中包含另一个或多个完整的select语句。
子查询的语法规则:
按照子查询返回的结果,可分为:
子查询出现的位置:
子查询出现在where或having子句中时,可以使用>、>=、<、<=、=、<>/!=等比较运算符或[not ]in、any/some、all、[not ]exists等操作符进行条件筛选。
标量子查询:查询基本工资高于公司平均工资的员工信息
mysql> select *
-> from emp
-> where sal > (select avg(sal) from emp);
+-------+-------+-----------+------+------------+------+------+--------+
| empid | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+-----------+------+------------+------+------+--------+
| 7566 | jones | manager | 7839 | 1981-04-02 | 2975 | NULL | 20 |
| 7698 | blake | manager | 7839 | 1981-05-01 | 2850 | NULL | 30 |
| 7782 | clark | manager | 7839 | 1981-06-09 | 2450 | NULL | 10 |
| 7788 | scott | analyst | 7566 | 1987-04-19 | 3000 | NULL | 20 |
| 7839 | king | persident | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7902 | ford | analyst | 7566 | 1981-12-03 | 3000 | NULL | 20 |
+-------+-------+-----------+------+------------+------+------+--------+
行子查询:查询和smith同部门同职位的员工
mysql> select *
-> from emp
-> where (deptno,job) = (select deptno,job from emp where ename='smith') and ename <> 'smith';
+-------+-------+-------+------+------------+------+------+--------+
| empid | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+-------+------+------------+------+------+--------+
| 7876 | adams | clerk | 7788 | 1987-05-23 | 1100 | NULL | 20 |
+-------+-------+-------+------+------------+------+------+--------+
列子查询:where子句中可使用[not ]in、any/some、all、[not ]exists等操作符进行条件筛选。
[not ]in子查询:查询普通员工的信息
mysql> select *
-> from emp
-> where empid not in (select mgr from emp where mgr is not null);
+-------+--------+----------+------+------------+------+------+--------+
| empid | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+----------+------+------------+------+------+--------+
| 7369 | smith | clerk | 7902 | 1980-12-17 | 800 | NULL | 20 |
| 7499 | allen | salesman | 7698 | 1981-02-20 | 1600 | 300 | 30 |
| 7521 | ward | salesman | 7698 | 1981-02-22 | 1250 | 500 | 30 |
| 7654 | martin | salesman | 7698 | 1981-09-28 | 1250 | 1400 | 30 |
| 7844 | turner | salesman | 7698 | 1981-09-08 | 1500 | 0 | 30 |
| 7876 | adams | clerk | 7788 | 1987-05-23 | 1100 | NULL | 20 |
| 7900 | james | clerk | 7698 | 1981-12-03 | 950 | NULL | 30 |
| 7934 | miller | clerk | 7782 | 1982-01-23 | 1300 | NULL | 10 |
+-------+--------+----------+------+------------+------+------+--------+
any/some子查询:查询基本工资高于30号部门任意员工的员工信息
mysql> select *
-> from emp
-> where sal>any(select sal from emp where deptno=30) and deptno<>30;
+-------+--------+-----------+------+------------+------+------+--------+
| empid | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-----------+------+------------+------+------+--------+
| 7782 | clark | manager | 7839 | 1981-06-09 | 2450 | NULL | 10 |
| 7839 | king | persident | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7934 | miller | clerk | 7782 | 1982-01-23 | 1300 | NULL | 10 |
| 7566 | jones | manager | 7839 | 1981-04-02 | 2975 | NULL | 20 |
| 7788 | scott | analyst | 7566 | 1987-04-19 | 3000 | NULL | 20 |
| 7876 | adams | clerk | 7788 | 1987-05-23 | 1100 | NULL | 20 |
| 7902 | ford | analyst | 7566 | 1981-12-03 | 3000 | NULL | 20 |
+-------+--------+-----------+------+------------+------+------+--------+
all子查询:查询基本工资高于30号部门所有员工的员工信息
mysql> select *
-> from emp
-> where sal>all(select sal from emp where deptno=30);
+-------+-------+-----------+------+------------+------+------+--------+
| empid | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+-----------+------+------------+------+------+--------+
| 7566 | jones | manager | 7839 | 1981-04-02 | 2975 | NULL | 20 |
| 7788 | scott | analyst | 7566 | 1987-04-19 | 3000 | NULL | 20 |
| 7839 | king | persident | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7902 | ford | analyst | 7566 | 1981-12-03 | 3000 | NULL | 20 |
+-------+-------+-----------+------+------------+------+------+--------+
exists子查询:如果dept表中存在30号部门则查询该部门的员工信息
mysql> select *
-> from emp
-> where deptno=30 and exists (select deptno from dept where deptno=30);
+-------+--------+----------+------+------------+------+------+--------+
| empid | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+----------+------+------------+------+------+--------+
| 7499 | allen | salesman | 7698 | 1981-02-20 | 1600 | 300 | 30 |
| 7521 | ward | salesman | 7698 | 1981-02-22 | 1250 | 500 | 30 |
| 7654 | martin | salesman | 7698 | 1981-09-28 | 1250 | 1400 | 30 |
| 7698 | blake | manager | 7839 | 1981-05-01 | 2850 | NULL | 30 |
| 7844 | turner | salesman | 7698 | 1981-09-08 | 1500 | 0 | 30 |
| 7900 | james | clerk | 7698 | 1981-12-03 | 950 | NULL | 30 |
+-------+--------+----------+------+------------+------+------+--------+
表子查询:查询各部门工资最高的员工
mysql> select emp.*
-> from emp
-> join (select deptno,max(sal) as max_sal from emp group by deptno) as t
-> on emp.deptno=t.deptno
-> where sal=max_sal;
+-------+-------+-----------+------+------------+------+------+--------+
| empid | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+-----------+------+------------+------+------+--------+
| 7698 | blake | manager | 7839 | 1981-05-01 | 2850 | NULL | 30 |
| 7788 | scott | analyst | 7566 | 1987-04-19 | 3000 | NULL | 20 |
| 7839 | king | persident | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7902 | ford | analyst | 7566 | 1981-12-03 | 3000 | NULL | 20 |
+-------+-------+-----------+------+------------+------+------+--------+
MySQL从4.1版本开始支持子查询,使用子查询进行SELECT语句嵌套查询,可以一次完成很多逻辑上需要多个步骤才能完成的SQL操作。
子查询虽然很灵活,但是执行效率并不高。执行子查询时,MySQL需要为内层子查询的查询结果建立一个临时表,然后外层主查询在临时表上进行查询和筛选。查询完毕后再撤销这些临时表,这里多了一个创建和销毁临时表的过程。因此,子查询的速度会受到一定的影响,如果查询的数据量比较大,这种影响就会随之增大。
优化方法:可以使用连接查询(join)代替子查询,连接查询不需要建立临时表,因此其速度比子查询快。
所有的连接查询都可以替换为子查询,但并不是所有的子查询都可以用连接查询代替。当where子句中需要使用聚合函数作为筛选条件时,只能使用子查询。