MySQL基本操作整理(五)、子查询与分页查询、联合查询

本文整理MySQL中的子查询,相比于其他的分组查询、排序查询和连接查询等,子查询略微复杂。本文按照子查询出现的位置,将子查询分为主要的几类,并以实际的案例进行介绍,以求用形象的语言来表达清楚子查询。
本文重点介绍子查询为前五大部分,分页查询与联合查询比较简单,只要记住关键字LIMITUNION,基本就能使用了,放在后两部分分别介绍。

一、子查询介绍

含义

出现在其他语句中的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`
);

六、分页查询

分页查询(LIMIT)可以理解为每次查询一定数量的记录。
应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求。
语法:

select 查询列表
	from 表
	【join type join2
	on 连接条件
	where 筛选条件
	group by 分组字段
	having 分组后的筛选
	order by 排序的字段】
	limitoffset,】size;

注:offset为要显示条目的起始索引(起始索引从0开始)
	size 要显示的条目个数

特点:

  1. limit语句放在查询语句的最后
  2. 对于要分页显示的条目(提供页数page和条目数size),公式为:select 查询列表 from 表 limit (page-1)*size,size;

案例1:查询前五条员工信息

SELECT * FROM  employees LIMIT 0,5;
SELECT * FROM  employees LIMIT 5;

案例2:查询第11条至第25条员工信息

SELECT * FROM  employees LIMIT 10,15;

案例3:查询有奖金的员工信息,并且工资较高的前10名

SELECT * 
FROM employees 
WHERE commission_pct IS NOT NULL 
ORDER BY salary DESC 
LIMIT 10 ;

七、联合查询

联合查询(UNION)含义:将多条查询语句的结果合并为一个结果,当有重复记录时,默认自动去重
应用场景:要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致
语法:

查询语句1
union
查询语句2
union
...

特点:

  1. 要求多条查询语句的查询列数是一致的!
  2. 要求多条查询语句的查询的每一列的类型和顺序最好一致
  3. union关键字默认去重,如果使用union all 可以包含重复项

案例1:查询部门编号>90邮箱包含a的员工信息

#一般写法:
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;;
#联合查询:
SELECT * FROM employees  WHERE email LIKE '%a%'
UNION
SELECT * FROM employees  WHERE department_id>90;

案例2:查询中国用户中男性的编号与姓名以及外国用户中年男性的用户编号 与姓名

SELECT id,cname FROM t_ca WHERE csex='男'
UNION ALL
SELECT t_id,tname FROM t_ua WHERE tGender='male';
原创文章 97 获赞 43 访问量 1万+

猜你喜欢

转载自blog.csdn.net/shao_yc/article/details/106148760