9. 子查询

news2024/11/27 6:31:23

9.1 概述

​ 子查询指一个查询语句嵌套在另一个查询语句内部,这个特性从 MySQL 4.1 开始引入。

​ 从相对位置来说,子查询又被称为内查询,主查询又被称为外查询

9.1.1 子查询的结构

子查询的结构如下所示:

SELECT select_list
FROM table
WHERE expr operator
				(
                SELECT select_list
                FROM table
              );

说明:将在下面的内容中逐一细说

  • 子查询在主查询之前执行(不相关子查询执行一次,相关子查询执行多次)
  • 子查询的结果被主查询使用
  • 子查询要包含在一对括号内
  • 将子查询放在比较条件的右侧(规范,不遵守不会导致出错)
  • 单行操作符对应单行子查询,多行操作符对应多行子查询
  • 编写子查询 SQL 语句的技巧:① 从里往外写;② 从外往里写

9.1.2 子查询的分类

(1) 分类方式一

​ 根据内查询的结果,是返回一条还是多条记录,将子查询分为单行子查询多行子查询

  • 单行子查询:

    image-20230613212802052

  • 多行子查询:

    image-20230613212827100

(2) 分类方式二

​ 按照内查询是否被执行多次,将子查询划分为相关(或关联)子查询不相关(或非关联)子查询

  • 不相关子查询:子查询从数据表中查询得到结果,这个结果只执行一次,并且作为主查询的条件执行

  • 相关子查询:从外部查询开始,主查询“传给”子查询条件,然后子查询根据条件进行查询,并将结果反馈给外部,这个过程需要重复多次

9.2 单行子查询

9.2.1 单行比较操作符

操作符含义
=等于
>大于
>=大于等于
<小于
<=小于等于
<>不等于

代码示例:

例题 1:查询工资大于 149 号员工工资的员工的信息

SELECT employee_id, last_name, salary
FROM employees
WHERE salary > (
		SELECT salary
		FROM employees
		WHERE employee_id = 149
		);

image-20230613221709284

​ 通常,我们将子查询放在比较条件的右侧。当然,放在左侧不会产生错误,只是不美观,如下所示:

SELECT employee_id, last_name, salary
FROM employees
WHERE  (
	    SELECT salary
	    FROM employees
	    WHERE employee_id = 149
	  ) < salary;

9.2.2 HAVING中使用子查询

执行的流程:

  • 首先执行子查询
  • 向主查询中的 HAVING 子句返回结果

例题 2:查询最低工资大于 110 号部门最低工资的部门 id 和其最低工资

SELECT department_id, MIN(salary)
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
HAVING MIN(salary) > (
		      SELECT MIN(salary)
		      FROM employees
		      WHERE department_id = 110
		     );

image-20230613215559379

9.2.3 CASE中的子查询

例题 3:显示员工的 employee_id、last_name 和 location。其中,若员工 department_id 与 location_id 为 1800 的 department_id 相同,则 location 为 “Canada”,其余则为 “USA”

SELECT employee_id, last_name, CASE department_id WHEN (
												    SELECT department_id
							                    FROM departments
							                    WHERE location_id = 1800
							                 ) THEN 'Canada' 
						                ELSE 'USA' END AS "location"
FROM employees;

image-20230613215902614

9.2.4 可能遇到的问题 1

(1) 空值问题

子查询返回的是空值,导致主查询失败。

SELECT last_name, job_id
FROM employees
WHERE job_id =
	     (
	        SELECT job_id
	        FROM employees
	        WHERE last_name = 'Haas'
	     );

说明:公司没 “Hass” 这个人,内查询的值为 NULL,导致外查询啥也查不到,如下图所示

image-20230613220301776

(2) 子查询包含在括号内

子查询必须包含在一对括号内,否则报错,如下所示:

SELECT employee_id, last_name, salary
FROM employees
WHERE salary > 
		SELECT salary
		FROM employees
		WHERE employee_id = 149;

强调:子查询要包含在一对括号内

(3) 非法使用操作符

在多行子查询中使用单行操作符,导致错误。

SELECT employee_id, last_name
FROM employees
WHERE salary =
	       (
	          SELECT MIN(salary)
		       FROM employees
		       GROUP BY department_id
	       );

说明:单行操作符对应单行子查询,多行操作符对应多行子查询。有时,由于没有预判到子查询会返回多条数据(多行子查询),而使用了单行操作符,导致出错,上述的查询结果如下

image-20230613220621622

由上面的几个例题可以体会到:

  • 子查询在主查询之前一次执行完成:无论主查询执行多少次,子查询都只执行一次,所有的主查询使用的都是子查询返回的那个唯一结果(不相关的单行子查询)

  • 子查询的结果被主查询使用

9.3 多行子查询

说明:

  • 子查询返回多条记录
  • 使用多行操作符

9.3.1 多行操作符

操作符含义
IN等于列表中的任一一个
ANY需要和单行比较操作符一起使用,和子查询返回的某一个值比较
ALL需要和单行比较操作符一起使用,和子查询返回的所有值比较
SOME实际上是 ANY 的别名,作用相同,一般常使用 ANY

例题 1:返回其它 job_id 中比 job_id 为 “IT_PROG” 部门任一工资低的员工的员工号、姓名、job_id以及 salary。

SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < ANY (
		           # 查询部门 id 为 "IT_PROG" 的员工的工资
    				 SELECT salary
		           FROM employees
		           WHERE job_id = 'IT_PROG'
		        )
AND job_id <> 'IT_PROG';

image-20230614204943221

说明:“任一”、“低” 表示只要小于 “IT_PROG” 部门中的最高工资即可,等价代码如下

SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < (
    		  # 查询'IT_PROG'部门的最高员工工资
		     SELECT MAX(salary)
		     FROM (
			       # 查询'IT_PROG'部门的员工工资 
                SELECT salary
			       FROM employees
			       WHERE job_id = 'IT_PROG'
			     ) it_prog_sal
) AND job_id <> 'IT_PROG';

例题 2:返回其它 job_id 中比 job_id 为 “IT_PROG” 部门所有工资低的员工的员工号、姓名、job_id 以及 salary。

SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary < ALL (
		           SELECT salary
		           FROM employees
		           WHERE job_id = 'IT_PROG'
		        )
AND job_id <> 'IT_PROG';

image-20230614205244758

说明:“所有” 意味着要低于 “IT_PROG” 部门中的最低工资。

附:可以使用 ALL 搭配 “<=” 或 “>=” 求解最小值或最大值。

例题 3:查询平均工资最低的部门 id

(1) 常规思路:求每个部门的平均工资 → 求最小的平均工资 → 求哪个部门的平均工资是这个最小值

# 求哪个部门的平均工资是这个最小值			  
SELECT department_id # 50
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
			      # 求最小的平均工资
			      SELECT MIN(avg_sal)
			      FROM (
				       # 求每个部门的平均工资
				       SELECT AVG(salary) AS "avg_sal"
				       FROM employees
				       GROUP BY department_id
				       ) t_dept_avg_sal
		         );

本题可以使用 <= 和 ALL 来求解:

SELECT department_id
FROM employees
GROUP BY department_id
# <= ALL:只有最小值满足这个条件,所以这是在求最小值
HAVING AVG(salary) <= ALL (
			           # 求所有部门的平均工资
			           SELECT AVG(salary)
			           FROM employees
			           GROUP BY department_id
			         );

9.3.2 可能遇到的问题 2

空值问题

​ 如下面代码所示,在子查询中包含一个 NULL 值,而 NOT IN 要与子查询中的所有值比较,也就是会与 NULL 值运算,所以导致最终的查询为空

SELECT last_name
FROM employees
WHERE employee_id NOT IN (
			         SELECT manager_id
			         FROM employees
			         );

image-20230614210542610

​ 将 NOT IN 修改为 IN 会如何呢?成功查询,不报错。因为 IN 表示“任一”,只要与多行子查询返回结果中的一个值匹配就可以,不一定与子查询中的 NULL 值进行比较。

SELECT last_name
FROM employees
WHERE employee_id IN (
			      SELECT manager_id
			      FROM employees
			     );

image-20230614210756205

9.4 相关子查询

9.4.1 相关子查询的执行流程

​ 每执行一次主查询,子查询都要执行一次,并且在主查询之前执行。子查询使用主查询中的某个列,并返回给主查询某个(些)行。流程如下图所示:

image-20230614212028278

相关子查询的书写格式:

SELECT column1, column2, ...
FROM table1 outer
WHERE column1 operator
				   (
                   SELECT column1, column2
                   FROM table2 inner
                   WHERE inner.expr1 = outer.expr2
                );

例题 1:查询员工中工资大于本部门平均工资的员工的 last_name、salary 及其 department_id

SELECT last_name, salary, department_id
FROM employees e1
WHERE salary > (
		      SELECT AVG(salary)
		      FROM employees e2
		      WHERE e2.`department_id` = e1.`department_id`
		     );

image-20230614212700493

说明:由这个例题可以看出

  • 子查询在主查询之前执行,并且需要执行多次
  • 子查询的结果被主查询使用
  • 子查询包含在一对小括号内

9.4.2 FROM中使用相关子查询

例题 1 可以改写为多表连接的形式:

SELECT e.`last_name`, e.`salary`, e.`department_id`
# 两张表连接,求笛卡尔积
FROM employees e, (
		        # “虚拟表”:部门 + 部门平均工资
		        SELECT department_id, AVG(salary) AS "dept_avg_sal"
		        FROM employees
		        GROUP BY department_id
		       ) t_dept_avg_sal
# 过滤数据:只有 department_id 相同的才可以拼接为一行数据
WHERE e.`department_id` = t_dept_avg_sal.`department_id`
# 一行中,员工的工资需大于部门平均工资
AND e.`salary` > t_dept_avg_sal.dept_avg_sal;

说明:子查询作为 FROM 从句中的一张“虚拟表”,要使用一对小括号 ( ) 括起来,而且必须要为“虚拟表”起别名,否则报错,如下所示:

image-20230614213544769

9.4.3 ORDER BY中使用相关子查询

例题 2:查询员工的 id、salary,按照 department_name 排序

SELECT employee_id, salary
FROM employees e
ORDER BY (
	     SELECT department_name
	     FROM departments d
	     WHERE d.`department_id` = e.`department_id`
	    );

image-20230614214037250

9.4.4 关键字EXISTSNOT EXISTS

​ 相关子查询通常也会与 EXISTS 关键字一起使用,用来检查在子查询中是否存在满足条件的行。

EXISTS:

  • 如果子查询中的当前行不满足条件,则条件返回 FALSE,并继续查询下一行
  • 如果子查询中的当前行满足条件,则条件返回 TRUE,并停止查找

附:NOT EXISTSEXISTS 相反,如果不满足条件则返回 TRUE,满足条件则返回 FALSE。

例题 3:查询公司管理者的 employee_id、last_name、job_id、department_id

SELECT employee_id, last_name, job_id, department_id
FROM employees e1
WHERE EXISTS (
		     SELECT *
		     FROM employees e2
		     WHERE e2.`manager_id` = e1.`employee_id`
	       );

image-20230614215128181

解释:对于当前员工 e 1 e_1 e1,如果他有下属 e 2 e_2 e2,这说明 e 1 e_1 e1 是管理者。

当然,还有其他方法:

方法二:自连接

SELECT DISTINCT emp.`employee_id`, emp.`last_name`, emp.`job_id`, emp.`department_id`
FROM employees mgr, employees emp
WHERE mgr.`manager_id` = emp.`employee_id`;

方法三:不相关的多行子查询,先查找出所有管理者的 employee_id,然后逐一核实每个员工是否为管理者中的一员

SELECT employee_id, last_name, job_id, department_id
FROM employees
# 逐一核实每个员工是否为管理者中的一员
WHERE employee_id IN (
 				 # 查找出所有管理者的 employee_id
		           SELECT DISTINCT manager_id
		           FROM employees
		         );

思考:方式二的自连接好还是方式一、方式三的子查询好?

  • 自连接好。在许多 DBMS 的处理过程中,对于自连接的处理速度要比子查询快得多,DBMS 将子查询转化成了自连接。

例题 4:查询 departments 表中,不存在于 employees 表中的部门的 department_id 和 department_name

SELECT d.`department_id`, d.`department_name`
FROM departments d
WHERE NOT EXISTS (
		         SELECT *
		         FROM employees e
		         WHERE e.`department_id` = d.`department_id`
	         ); 

image-20230614215840314

解释:NOT EXISTS 与 EXISTS 相反,子查询为 FALSE,即 employees 表中不存在当前这个 department_id 时,子查询返回 TRUE,主查询则返回当前这个 department_id 和 department_name。

附:这个题翻译一下就是求 departments 表相对于 employees 表中独有的数据,也就是求下图中的紫色区域,利用外连接的变式求解

image-20230606212532459

SELECT d.`department_id`, d.`department_name`
FROM employees e 
RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL;

总结:

  1. 由上面子查询的所有内容可知,在查询语句中,除了 GROUP BYLIMIT,其他位置都可以声明子查询。
  2. 子查询 SQL 编写技巧:
    • 如果子查询相对简单,则可以从外往里写;如果子查询较复杂,则从里往外写
    • 如果子查询是相关子查询,通常是从外往里写

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/651072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Apache Zeppelin系列教程第九篇——SQL Debug In Zeppelin

SQL Debug介绍 首先介绍下什么是SQL Debug&#xff1f; 但是经常有这样一个需求&#xff0c;一大段sql 跑出来之后&#xff0c;发现不是自己想要的结果&#xff1f;比如&#xff1a; demo 1: select id,name from ( select id,name from table1 union all select id,name fr…

数据库(第五章)数据库的完整性

1.数据库的正确性和相容性 正确性&#xff1a;符合现实逻辑 相容性&#xff1a;两个表中的同一对象要完全相同 如何实现数据库的完整性&#xff1f; 1.定义完整性约束条件 2.提供完整性检查方法 3.进行违约处理 完整性我们之前学过。包括三个 1.实体完整性 2.参照完整性 3.用户…

Fiddler中 AutoResponder 使用

Fiddler的 AutoResponder &#xff0c;即URL重定向功能非常强大。不管我们做URL重定向&#xff0c;还是做mock测试等&#xff0c;都可以通过该功能进行实践。 下面&#xff0c;小酋就来具体讲下该功能的用法。 如果你想学习Fiddler抓包工具&#xff0c;我这边给你推荐一套视频…

【C++进阶】带你手撕AVL树

文章目录 一、什么是AVL树二、AVL树的定义三、AVL树的插入1.理论讲解2.代码实现 四、AVL树的旋转1.左单旋2.右单旋3.左右双旋4.右左双旋 五、 AVL树的验证六、完整源码 一、什么是AVL树 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为…

Kafka集群部署

Kafka是一个高吞吐量、基于ZooKeeper&#xff08;ZooKeeper维护Kafka的broker信息&#xff09;的分布式发布订阅信息系统&#xff0c;它可以处理消费者在网站中的所有动作&#xff08;网页浏览&#xff0c;搜索和其他用户的行动&#xff09;流数据。通常情况下&#xff0c;使用…

Linux(CentOS 7)下安装配置Maven3.9.2

Linux&#xff08;CentOS 7&#xff09;下安装配置Maven3.9.2 环境 JDK 1.8OS:Centos 7.5 tar包安装 下载 apache-maven-3.9.2-bin.tar.gz https://maven.apache.org/download.cgi 安装步骤 公共服务&#xff0c;将maven安装在root用户下。 创建maven安装地址解压安装…

UE4/5样条线学习(三):样条线与时间轴

目录 简单的小模板 物品跟随样条线移动 粒子特效类&#xff1a; 简单的小模板 通过之前的案例&#xff0c;我们可以直接创建一个actor蓝图&#xff0c;加上要用的样条组件&#xff1a; 然后我们就可以通过时间轴做出不同的一些效果 在蓝图中找到时间轴的这个节点 双击时间…

Web安全测试中常见逻辑漏洞解析(实战篇)

前言&#xff1a; 越权漏洞是比较常见的漏洞类型&#xff0c;越权漏洞可以理解为&#xff0c;一个正常的用户A通常只能够对自己的一些信息进行增删改查&#xff0c;但是由于程序员的一时疏忽&#xff0c;对信息进行增删改查的时候没有进行一个判断&#xff0c;判断所需要操作的…

网络安全学习路线+自学笔记(超详细)

01 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面…

2023我的前端面试准备

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 回顾过往&#xff0c;至今已有三个年头了&#xff0c;《2023年我的前端面试准备》在小…

我的创作纪念日——创作历程,机缘,与成就

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 写在前面 一.机缘 二.收获 三.日常 四.成就 五.憧憬与规划 六.分享 写在前面 大…

Vivado 下 IP核之FIFO 实验

目录 Vivado 下 IP核之FIFO 实验 1、FIFO IP 核简介 2、实验任务 3、程序设计 3.1、FIFO IP 核配置 3.1.1、“Basic” 选项卡下各参数配置 3.1.2、“Native Ports”选项卡下各参数配置 3.1.3、“Status Flags” 选项卡下各参数配置 3.1.4、“Data Counts&#xff08;数…

私域开始收费后,还值得做吗?

这些年&#xff0c;私域成为不少企业挖掘增量的新阵地。 私域指品牌自有的可随时、重复、低成本触达用户的场域。《2023私域运营趋势白皮书》&#xff08;后称<白皮书>&#xff09;显示&#xff0c;接受调研的企业中有70%将私域运营阵地放在腾讯旗下的企业微信。 不过&…

美的中间件面试(部分)

用redis有遇到过大key问题吗&#xff0c;怎么解决 我介绍项目中用异步解耦的思路来从mysql同步数据到redis&#xff0c;具体就是binlogkafaka。面试官问读的主mysql的binlog,还是从的binlog。A&#xff1a;主mysql。Q&#xff1a;可以用什么中间件读binlog。A&#xff1a;可以…

高效操作!linux在终端里快速跳转到文件管理器对应的目录下

在linux下&#xff0c;可能会有一个经常的操作&#xff0c;是在终端下&#xff0c;进入某个文件夹&#xff0c;用vim修改了某些操作&#xff0c;比如修改了一个html文件&#xff1b; 然后你想用chrome打开这个html文件&#xff0c;那么你可能需要打开文件管理器&#xff0c;切…

第五章 模型篇: 模型保存与加载

参考教程&#xff1a; https://pytorch.org/tutorials/beginner/basics/saveloadrun_tutorial.html 文章目录 pytorch中的保存与加载torch.save()torch.load()代码示例 模型的保存与加载保存 state_dict()nn.Module().load_state_dict()加载模型参数保存模型本身加载模型本身 c…

C语言编程语法—排序算法

一、冒泡排序 冒泡排序&#xff08;英语&#xff1a;Bubble Sort&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序&#xff08;如从大到小、首字母从A到Z&#xff09;错误就把他们交换过来。 过程演示&…

ansible playbook脚本,安装LAMP套件

yum 集中式安装lamp --- - name: LAMP installhosts: dbserverstasks:- name: disable firewalldservice:name: firewalldenabled: nostate: stopped- name: disabled selinuxshell: "sudo sed -i s/SELINUXenforcing/SELINUXdisabled/g /etc/selinux/config"shell:…

Random random = SecureRandom.getInstanceStrong();堵塞线程问题解决

sonar扫描到使用Random随机函数不安全, 推荐使用SecureRandom替换&#xff0c;就是他–》【SecureRandom.getInstanceStrong()】&#xff0c;分别在本地&#xff0c;测试环境测过没问题上生产&#xff0c;但是运行了一段时间突然报错&#xff01;&#xff01;&#xff01; 然后…

简要介绍 | 基于双风机振动的燕麦清选与筛选

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对基于双风机振动的燕麦清选和筛选装置设计与仿真进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 注2&#xff1a;"简要介绍"系列的所有创作均使用了AIGC工具辅助 基于双风机振动的燕…