本文整理MySQL中的子查询,相比于其他的分组查询、排序查询和连接查询等,子查询略微复杂。本文按照子查询出现的位置,将子查询分为主要的几类,并以实际的案例进行介绍,以求用形象的语言来表达清楚子查询。
一、子查询介绍
含义
出现在其他语句中的select语句,称为子查询或内查询(其他语句可以是增删改等);外部的查询语句,称为主查询或外查询。
分类
1.按子查询出现的位置分类:
- select后:仅仅支持标量子查询
- from后:支持表子查询
- where或having后:支持标量子查询(单行子查询)、列子查询(多行子查询)以及行子查询
- exists后(称为相关子查询)
2.按结果了集的行列数分类,分为如下几类:
- 标量子查询(结果集只有一行一列)
- 列子查询(结果集只有一列多行)
- 行子查询(结果集有一行多列)
- 表子查询(结果集一般为多行多列)
重点为:位于where或者having后面的标量子查询与列子查询
二、WHERE或HAVING后的子查询
1. 标量子查询
将查询结果作为标量使用:
案例1:查询员工表中工资比 Abel高的员工信息
SELECT *
FROM employees
WHERE salary > (
SELECT salary
FROM employees
WHERE last_name = 'Abel'
);
查询时可以添加多个子查询作为筛选条件:
案例2:查询员工表中,job_id与141号员工相同,salary比143号员工高的员工的姓名,job_id 和工资
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:查询最低工资大于50号部门最低工资的部门id和其最低工资
#初步实现可以分三步走:
#(1)查询50号部门的最低工资
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
#(2)查询每个部门的最低工资
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
#(3)在(2)基础上筛选,满足min(salary)>(1)
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
);
2. 列子查询
案例1:返回location_id是1400或1700的部门中的所有员工姓名
#(1)查询location_id是1400或1700的部门编号
SELECT DISTINCT department_id
FROM departments
WHERE location_id IN(1400,1700)
#(2)查询员工姓名,要求部门号是(1)列表中的某一个
SELECT last_name
FROM employees
WHERE department_id IN(
SELECT DISTINCT department_id
FROM departments
WHERE location_id IN(1400,1700)
);
案例2:返回其它工种中比job_id为‘IT_PROG’工种任一工资低的员工的员工号、姓名、job_id 以及salary
分析:比任一工资低的,那么只要低于最大的就行了。如a < any(10, 20, 30)等价于 a < max(10, 20, 30)
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (
SELECT MAX(salary)
FROM employees
WHERE job_id = 'IT_PROG'
)AND job_id<>'IT_PROG';
或者
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<ANY(
SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
案例3:返回其它工种中比job_id为‘IT_PROG’工种所有工资都低的员工 的员工号、姓名、job_id 以及salary
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (
SELECT MIN(salary)
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id <> 'IT_PROG';
或者
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<ALL(
SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
3. 行子查询(了解)
行子查询的结果集为一行多列或多行多列
案例:查询员工编号最小并且工资最高的员工信息
一般做法:
SELECT *
FROM employees
WHERE employee_id=(
SELECT MIN(employee_id)
FROM employees
)AND salary=(
SELECT MAX(salary)
FROM employees
);
行子查询:
SELECT *
FROM employees
WHERE (employee_id,salary)=(
SELECT MIN(employee_id),MAX(salary) #结果集为一行多列
FROM employees
);
三、SELECT后的子查询
说明:select后的子查询仅仅支持标量子查询
案例:查询每个部门的员工个数
效果如下图,前面为departments表的信息,而departments表中必定有些部门在employees表中没有员工与其对应,则个数为0,如下半部分。
#分两步分析:
#(1)如下效果为每个部门的个数都是总员工数
SELECT d.*,(
SELECT COUNT(*)
FROM employees e
) 个数
FROM departments d;
#(2)筛选条件为员工表的部门id等于部门表的部门id
SELECT d.*,(
SELECT COUNT(*)
FROM employees e
WHERE e.department_id = d.`department_id`
) 个数
FROM departments d;
四、FROM后的子查询
说明:将子查询结果充当一张表,要求必须起别名,否则找不到
案例:查询每个部门的平均工资的工资等级
分析:(1)查询每个部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
#查询工资等级表
SELECT * FROM job_grades;
(2) 连接(1)的结果集和job_grades表,筛选条件为平均工资 between lowest_sal and highest_sal
SELECT ag_dep.*,g.`grade_level`
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;
五、exists后的子查询(相关子查询)
exists后的子查询也称为相关子查询。
语法为:exists (完整的查询语句)
结果:1或0
#举例:以下查询结果为 1
SELECT EXISTS(SELECT employee_id FROM employees);
案例1:查询有员工的部门名
分析:(1)查询部门表的所有部门id和部门名
SELECT d.`department_id`, department_name
FROM departments d
(2)在该结果集上筛选出有员工的(可以查询任意信息,因为只要判断是否存在即可)
SELECT d.`department_id`, department_name
FROM departments d
WHERE EXISTS(
SELECT *
FROM employees e
WHERE d.`department_id`=e.`department_id` #子查询涉及到主查询的字段,所以称相关子查询
);
能用EXISTS的绝对可以用IN来实现:
SELECT department_name
FROM departments d
WHERE d.`department_id` IN(
SELECT department_id
FROM employees
);
案例2:查询没有女朋友的男神信息
# IN 方法:
SELECT bo.*
FROM boys bo
WHERE bo.id NOT IN(
SELECT b.`boyfriend_id`
FROM beauty b
);
# EXISTS 方法:
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS (
SELECT *
FROM beauty b
WHERE b.`boyfriend_id` = bo.`id`
);
转载:https://blog.csdn.net/shao_yc/article/details/106148760