MySQL 基础 ———— 子查询

引言

承接《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 emp
    WHERE 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 emp
    WHERE dept_id > 2
    AND 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 emp
    WHERE dept_id >= 2
    AND 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 
  * 
FROM
  dept d,
  (SELECT 
    MIN(avg_sal),
    dept_id 
  FROM
    (SELECT 
      AVG(salary) avg_sal,
      dept_id 
    FROM
      emp 
    GROUP BY dept_id) sal) lowest 
WHERE d.`id` = lowest.dept_id ;

其实这种子查询是完全可以简化的,比如通过排序或 LIMIT 子句等。

总结

SELECT 后面只支持标量子查询。

FROM 后面支持表子查询。

WHERE HAVING 后面支持(重点):标量子查询列子查询、行子查询(较少)。

EXISTS 后面支持表子查询。

发布了191 篇原创文章 · 获赞 280 · 访问量 52万+

猜你喜欢

转载自blog.csdn.net/u014745069/article/details/102886952
今日推荐