引言
承接《MySQL 基础 ———— 连接查询》,本文介绍和展示SQL中子查询的使用。
子查询是出现在其他语句中的select 语句,也称为内查询。外部的查询语句,称为主查询或外查询。
一、子查询的分类和支持的子句
按照子查询出现的位置,可以分为:
select 后面、from 后面、where 或 having 后面、exists 后面(相关子查询)。
按照子查询的结果集或者功能,可以分为:
1、标量子查询(单行子查询,结果集只有一行一列)
2、列子查询(多行子查询,结果集是一列多行)
3、行子查询(多列子查询,结果集是一行多列)
4、表子查询(结果集可能是多行多列)
各子句能够支持的子查询类型:
SELECT 后面只支持标量子查询。
FROM 后面支持表子查询。
WHERE 或 HAVING 后面支持(重点):标量子查询、列子查询、行子查询(较少)。
EXISTS 后面支持表子查询。
二、子查询的特点
1、子查询都要放在小括号里
2、子查询一般放在条件的右侧
3、标量子查询一般配合单行操作符使用:> < >= <= <> 等,列子查询一般配合多行操作符使用:IN ANY/SOME ALL 等。
三、标量子查询(单行子查询)
标量子查询的结果集是单行单列,即一个确定的值。
员工表:
案例一:查询工资大于王强工资的员工信息。思路:第一步,先查询王强的工资,第二步,通过 where子句,进行筛选。
SELECT * FROM emp WHERE salary >= (SELECT salary FROM emp WHERE emp_name = '王强'
);
案例二:查询部门与张建国的部门相同,工资大于孙岩工资的员工信息。
SELECT * FROM emp
WHERE dept_id = (SELECT dept_id FROM emp WHERE emp_name = '张建国'
)
AND salary > (SELECT salary FROM emp WHERE emp_name = '孙岩'
);
案例三:查询部门最低工资大于 1 号部门最低工资的部门id 和 最低工资。
思路:第一步,先查询 1 号部门的最低工资,第二步,根据上一步的结果集进行条件筛选。
SELECT dept_id, MIN(salary) 部门最低工资
FROM emp
GROUP BY dept_id
HAVING MIN(salary) > (SELECT MIN(salary)FROM empWHERE dept_id = 1
);
四、列子查询(多行子查询)
列子查询返回一列多行,结果集可以看做是某个字段的值的集合,比如 dept_id = {1, 2, 5}。可以配合 IN 、NOT IN 等多行比较操作符一起使用。
常见的多行比较操作符有:
操作符 | 含义 |
IN / NOT IN | 等于或不等于结果集中的任意一个 |
ANY / SOME | 和子查询返回的某一个值比较 |
ALL | 和子查询返回的所有值比较 |
其中,IN 和 NOT IN 使用频率非常高,ANY 和 SOME 含义相同,但是可读性较差,含义容易混淆,而且可以使用其他方式代替,因此不常使用。例如:a > ANY(10, 20, 30) ,可以替换为:a > MIN(10, 20, 30)。
员工表:
案例一:查询孙姓员工所在部门的全部员工信息。
SELECT * FROM emp
WHERE dept_id IN(SELECT dept_id FROM emp WHERE emp_name LIKE '孙%'
);
五、行子查询(一行多列)
行子查询一定会查询出多个列值,这就要求语法有一定的变化,有点类似多个筛选条件,比如:
案例一:查询员工编号最小,且工资最高的员工:
SELECT * FROM emp
WHERE (emp_id, salary) = (SELECT MIN(emp_id), MAX(salary) FROM emp
)
案例二:查询部门id > 2, 且工资 > 8000 的员工信息:
SELECT * FROM emp
WHERE (dept_id, salary) = (SELECT dept_id, salary FROM empWHERE dept_id > 2AND salary > 8000
)
行子查询要求子查询结果集必须只有一条记录,而且查询结果的各个值必须与参数括号里面的各个值对应。由于行子查询本身可以通过其他语句替代,加之在一条记录之上再做查询没什么实际意义,所以应用场景非常有限。
六、表子查询(多行多列)
表子查询一般放在 from 子句后面,充当一个小型的结果集,可以进行常规的筛选,甚至是连接查询。但要求必须要给子查询起别名。
案例:查询部门编号大于2, 且工资大于5000 的员工姓名、工资、部门编号、部门名称。
SELECT e.emp_name, e.salary, e.dept_id, d.`dept_name`
FROM (SELECT `emp_name`, dept_id, salary FROM empWHERE dept_id >= 2AND salary > 5000
) e
LEFT JOIN dept d
ON e.dept_id = d.`id`;
整体查询结果:
表子查询也是比较常用的子查询类型。
EXISTS 后面的表子查询
exists()函数值关心参数中是否有值,如果有则输出1, 如果没有就是0.
SELECT EXISTS(SELECT * FROM emp)
另外,EXISTS 函数还可以用于 WHERE 子句后面,含义是“当存在/不存在时执行查询”,效果类似于 COUNT > 0 或 COUNT = 0
SELECT *
FROM dept
WHERE EXISTS(SELECT * FROM emp)
SELECT *
FROM dept
WHERE NOT EXISTS(SELECT * FROM emp)
七、子查询经典案例
员工表:
部门表:
案例一:查询工资最低的员工信息:
SELECT * FROM emp
WHERE salary = (SELECT MIN(salary) FROM emp
)
案例二:查询部门平均工资最低的部门信息:
SELECT *
FROMdept d,(SELECT MIN(avg_sal),dept_id FROM(SELECT AVG(salary) avg_sal,dept_id FROMemp GROUP BY dept_id) sal) lowest
WHERE d.`id` = lowest.dept_id ;
其实这种子查询是完全可以简化的,比如通过排序或 LIMIT 子句等。
总结
SELECT 后面只支持标量子查询。
FROM 后面支持表子查询。
WHERE 或 HAVING 后面支持(重点):标量子查询、列子查询、行子查询(较少)。
EXISTS 后面支持表子查询。