子查询又称内查询,指一个查询语句S1嵌套在另一个查询语句S2的内部的查询,其中,外部的S2查询称为主查询或外查询。
在 SELECT 子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。
子查询的结果被主查询使用,因此写sql的关键是分析子查询及其子查询的结果类型,之后分步骤进行
分类
按子查询结果集的行列数不同
- 标量 子查询(也称单行子查询,子查询结果集只有一行一列)
- 列 子查询(子查询结果集有一列多行)
- 行 子查询(子查询结果集有一行多列)
- 表 子查询(子查询结果集一般为多行多列)
按子查询出现的位置
-
select后面。
只支持标量子查询。因为select之后是查询列表,只能用一行一列的标量子查询。
-
from后面。支持表子查询。
-
where或having后面。支持标量子查询、列子查询、行子查询。
-
exists后面(相关子查询)。支持表子查询。
特点
- 子查询都放在小括号内,其执行顺序先于主查询,exists后的子查询特殊。
- 子查询一般放在条件表达式的右侧。
- 标量子查询一般搭配着单行操作符(条件运算符等)使用。
- 列子查询,一般搭配多行操作符(in、any、some、all)使用。
子查询在where后
标量子查询
子查询的结果是一行一列的值,可用在where和having后的条件表达式。
因为子查询的执行顺序先于主查询,且其查询结果被主查询使用,因此写sql时首先要找到子查询。
实例1 查询谁的工资比Abel高
-
分析实例可知,主查询需要用到Abel的工资,即子查询是查出Abel的工资
#查询Abel的工资,一行一列 SELECT salary FROM employees WHERE last_name = 'Abel'
-
将子查询结果嵌套进主查询。
# 查询谁的工资比Abel高
SELECT * FROM employees WHERE salary > (
SELECT salary
FROM employees
WHERE last_name = 'Abel'
);
实例2 查询job_id与141号员工相同,salary比143号员工多的员工姓名、工资
此例where后有两个判断条件,需要两个子查询。
#查询job_id与141号员工相同,salary比143号员工多的员工姓名、工资
SELECT last_name, job_id,salary
FROM employees
WHERE job_id = (
SELECT job_id
FROM employees
WHERE employee_id = 141
)
AND salary >
(
SELECT salary
FROM employees
WHERE employee_id = 143
)
实例3 在having子句中使用子查询
#在having子句中使用子查询
SELECT department_id,MIN(salary) mm
FROM employees
GROUP BY department_id
HAVING mm > (
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
)
列子查询(多行子 查询)
in子查询
用于判断一个给定值是否存在于子查询的结果集中。其语法格式:
<表达式> [NOT] IN <子查询>
语法说明如下。
<表达式>
:用于指定表达式。当表达式与子查询返回的结果集中的某个值相等时,返回 TRUE,否则返回 FALSE;若使用关键字 NOT,则返回的值正好相反。<子查询>
:用于指定子查询。这里的子查询只能返回一列数据。对于比较复杂的查询要求,可以使用 SELECT 语句实现子查询的多层嵌套。
#查询location_id是1400或1700的部门的所有员工姓名
SELECT last_name
FROM employees
WHERE department_id IN (
SELECT department_id
FROM departments
WHERE location_id IN ('1400','1700')
)
比较运算符子查询
比较运算符所使用的子查询主要用于对表达式的值和子查询返回的值进行比较运算。其语法格式为:
<表达式> {= | < | > | >= | <= | <=> | < > | != }
{ ALL | SOME | ANY} <子查询>
语法说明如下。
<子查询>
:用于指定子查询。<表达式>
:用于指定要进行比较的表达式。ALL
、SOME
和ANY
:可选项。用于指定对比较运算的限制。其中,关键字 ALL 用于指定表达式需要与子查询结果集中的每个值都进行比较,当表达式与每个值都满足比较关系时,会返回 TRUE,否则返回 FALSE;关键字 SOME 和 ANY 是同义词,表示表达式只要与子查询结果集中的某个值满足比较关系,就返回 TRUE,否则返回 FALSE。
子查询在select后
select后有子查询,也就是主查询的查询列表中包含子查询,且该子查询必须是标量子查询(一行一列)。
实例 查询每个部门的员工个数
分析:题目涉及部门表和员工表,且需要每个部门,则说明主表是部门表。若主表是员工表,则可能无法显示员工数为0的部门
#查询每个部门的员工个数
SELECT d.* ,(
SELECT COUNT(*)
FROM employees e
WHERE d.department_id = e.`department_id` //根据部门id统计部门员工数
) 员工个数
FROM departments d
#使用连接查询实现。这两方法的性能
SELECT d.*,IF(ISNULL(e.department_id),0,COUNT(*))
FROM departments d
LEFT OUTER JOIN employees e
ON e.department_id = d.department_id
GROUP BY d.department_id
子查询在from后
一般情况下,from子句中包含一个数据表,因此若子查询返回一个数据表,可以放在from后面。
实例 查询每个部门的平均工资的工资等级。
分析:平均工资需要执行一次查询,返回部门和对应的平均工资的数据表。之后该表和工资等级表做连接查询
#步骤一 查询每个部门的平均工资,结果是一个 两列多行 中间表
select avg(salart),department_id
from empoyees
group by department_id
#步骤二 中间表和工资等级表做连接
select avg_dep.* ,jg.grade_leave
from (
select avg(salart) as avg_s,department_id
from empoyees
group by department_id
) as avg_dep //中间表必须起别名
left inner join job_grade as jg
where avg_dep.acg_s between jg.min and jg.max
子查询在exsits后(相关子查询)
关键字 EXIST 所使用的子查询是判断语句,主要用于判断子查询的结果集是否为空。其语法格式为:
EXIST <子查询>
若子查询的结果集不为空,则返回 TRUE;否则返回 FALSE。
实例 查询有员工的部门名
#查询有员工的部门名
SELECT department_name
FROM departments d
WHERE EXISTS(
SELECT 1 //只判断有没有,不关心子查询的查询列表,因此可以用1
FROM employees e
WHERE d.department_id = e.`department_id`
)
#查询没有女朋友的男生信息
SELECT b.*
FROM boys AS b
WHERE NOT EXISTS( //不存在用not关键字
SELECT 1
FROM beauty be
WHERE b.`id` = be.`boyfriend_id`
)
exists子查询的执行顺讯不同于其他子查询(先子后主),其执行顺序(先主后子)如下
- 执行一次外部查询
SELECT department_nameFROM departments d
,缓存中间结果集。 - 遍历结果上一步的中间结果集,逐个判断每条数据是否使exists返回true,若为true则放入最终结果集。