MySQL 子查询

文章目录

  • 1.简介
  • 2.优势
  • 3.分类
    • 3.1 标量子查询
    • 3.2 行子查询
    • 3.3 列子查询
      • IN 操作符
      • ALL 操作符
      • ANY/SOME 操作符
    • 3.4 表子查询
  • 4.关联子查询
  • 5.EXISTS 和 NOT EXISTS
  • 6.横向派生表
  • 7.附录
  • 参考文献

1.简介

子查询是另一个语句中的 SELECT 语句。

子查询也称为内查询(Inner Query),必须位于括号之中。包含子查询的查询称为外查询(Outer Query)。子查询支持多层嵌套,也就是子查询可以包含其他子查询。

子查询的外部语句可以是以下任一语句:SELECT、INSERT、UPDATE、DELETE、SET 或 DO。

下面是一个示例。

SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);

在这个例子中,SELECT * FROM t1 ...是外查询,位于括号中的SELECT column1 FROM t2是子查询。

2.优势

使用子查询的主要优势有:

  • 它们允许结构化查询,以便可以隔离语句的每个部分。
  • 它们提供了需要复杂连接和并集的操作的替代方法。
  • 许多人发现子查询比复杂的连接或联合更具可读性。 事实上,正是子查询的创新让人们产生了将早期 SQL 称为“结构化查询语言”的最初想法。

3.分类

根据子查询的结果可以将其分为多种类型。

  • 标量子查询(Scalar Subquery):返回单个值(一行一列)的子查询。
  • 行子查询(Row Subquery):返回单行结果(一行多列)的子查询。
  • 列子查询(Column Subquery):返回单列结果(一列多行)的子查询。
  • 表子查询(Table Subquery):返回一个虚拟表(多行多列)的子查询。

3.1 标量子查询

标量子查询的结果就像一个常量一样,可以用于 SELECT、WHERE、GROUP BY、HAVING 以及 ORDER BY 等子句中。

例如以下语句返回了月薪大于平均月薪的员工:

SELECT name, salary
FROM employee
WHERE salary > (SELECT AVG(salary)FROM employee
);+--------+---------+
| name   | salary  |
+--------+---------+
| 刘备   | 1000000 |
| 曹操   | 2000000 |
| 孙权   | 1500000 |
+--------+---------+

其中,括号内部的子查询用于获得员工的平均月薪,外查询用于返回月薪大于平均月薪的员工信息。

3.2 行子查询

行子查询可以当作一个一行多列的临时表使用。

以下语句查找所有与“关羽”在同一个部门并且职级相同的员工:

SELECT name, dept_id, job_level
FROM employee
WHERE (dept_id, job_level) = (SELECT dept_id, job_levelFROM employeeWHERE name = '关羽')
AND name != '关羽';+--------+---------+-----------+
| name   | dept_id | job_level |
+--------+---------+-----------+
| 张飞   |       1 |         2 |
+--------+---------+-----------+

3.3 列子查询

列子查询可以当作一个一列多行的临时表使用。

当 WHERE 条件中的子查询返回多行数据时,不能再使用普通的比较运算符,因为它们不支持单个值和多个值的比较;如果想要判断某个字段是否在子查询返回的数据列表中,可以将子查询与 IN、ALL、ANY/SOME 操作符配合使用。

operand IN (subquery)
operand comparison_operator ALL (subquery)
operand comparison_operator ANY (subquery)
operand comparison_operator SOME (subquery)

其中 comparison_operator 是下面运算符之一:

=  >  <  >=  <=  <>  !=

IN 操作符

IN 操作符表示表达式是否在子查询的结果列中,如果在,如返回 TRUE。

SELECT s1 FROM t1 WHERE s1 IN (SELECT s1 FROM t2);

比如查找“刘备”和“孙权”所在部门的员工:

SELECT name, dept_id
FROM employee
WHERE dept_id IN (SELECT dept_idFROM employeeWHERE name IN ('刘备','孙权')
)
AND name NOT IN ('刘备','孙权');+-----------+---------+
| name      | dept_id |
+-----------+---------+
| 关羽      |       1 |
| 张飞      |       1 |
| 黄月英    |       1 |
| 吕蒙      |       3 |
| 黄盖      |       3 |
+-----------+---------+

NOT IN 操作符执行和 IN 相反的操作,也就是当表达式在列子查询结果中时为 TRUE。

ALL 操作符

除了 IN 运算符之外,ALL、ANY/SOME 运算符与比较运算符的结合也可以用于判断子查询的返回结果。

ALL 必须跟在比较运算符之后,如果表达式与子查询返回列中的所有值的比较结果为 TRUE,则返回 TRUE。

SELECT s1 FROM t1 WHERE s1 > ALL (SELECT s1 FROM t2);

对于 ALL 操作符,有两个需要注意的情况,就是子查询结果为空或者存在 NULL 值。

SELECT name, salary
FROM employee
WHERE salary > ALL (SELECT 999999 FROM anonymity WHERE 1=0);

以上查询会返回所有员工,因为子查询返回结果为空集,外查询相当于没有 WHERE 条件。

SELECT name, salary
FROM employee
WHERE salary > ALL (SELECT MAX(999999) FROM anonymity WHERE 1=0);

以上查询会返回返回空集,因为子查询返回 NULL,任何数值和 NULL 比较的结果都是未知(unknown)。

NOT IN 是 <> ALL 的别名。 因此,这两个语句是相同的:

SELECT s1 FROM t1 WHERE s1 <> ALL (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 NOT IN (SELECT s1 FROM t2);

ANY/SOME 操作符

SOME 是 ANY 的别名,所以 SOME 等同于 ANY。

ANY 关键字必须跟在比较运算符之后,如果表达式与子查询返回列中的任何值的比较结果为 TRUE,则返回 TRUE。

SELECT s1 FROM t1 WHERE s1 > ANY (SELECT s1 FROM t2);

= ANY 和 IN 操作符等价。

3.4 表子查询

当子查询返回的结果包含多行多列数据时,称为表子查询。表子查询通常用于 FROM 子句或者查询条件中。

当子查询出现在 FROM 子句中时,相当于创建了一个语句级别的派生表(Derived Table)。

SELECT ... FROM (subquery) [AS] tbl_name ...

JSON_TABLE() 函数生成一个表,并提供另一种创建派生表的方法:

SELECT * FROM JSON_TABLE(arg_list) [AS] tbl_name ...

[AS] tbl_name 子句是强制性的,因为 FROM 子句中的每个表都必须有一个名称。 派生表中的任何列都必须具有唯一名称。tbl_name 后面可以跟一个带括号的派生表列名称列表。

SELECT ... FROM (subquery) [AS] tbl_name (col_list) ...

列名数量必须与列数量相同。

4.关联子查询

在上面的示例中,子查询和外查询之间没有联系,可以单独运行。这种子查询也称为非关联子查询(Non-correlated Subquery)。

另一类子查询会引用外查询中的字段,从而与外部查询产生关联,也称为关联子查询(Correlated Subquery)。

例如以下示例通过使用关联子查询获得各个部门的员工数量:

SELECT d.name AS "部门名称",(SELECT count(*)FROM employeeWHERE dept_id = d.id) as "员工数量"
FROM department d;+--------------+--------------+
| 部门名称     | 员工数量     |
+--------------+--------------+
| 蜀汉部       |            4 |
| 曹魏部       |            3 |
| 孙吴部       |            3 |
+--------------+--------------+

其中,子查询的 WHERE 条件中使用了外查询的部门编号(d.id),从而与外查询产生关联。该语句执行时,外查询先检索出所有的部门数据,针对每条记录再将 d.id 传递给子查询,子查询返回每个部门的员工数量。

5.EXISTS 和 NOT EXISTS

如果子查询返回任何行,则 EXISTS 子查询为 TRUE,NOT EXISTS 子查询为 FALSE。

SELECT column1 FROM t1 WHERE EXISTS (SELECT * FROM t2);

传统上,EXISTS 子查询以 SELECT * 开头,但它也可以以 SELECT 5 或 SELECT column1 或任何其他内容开头。 MySQL 会忽略此类子查询中的 SELECT 列表,因此没有区别。

对于前面的示例,如果 t2 包含任何行,甚至只包含 NULL 值的行,则 EXISTS 条件为 TRUE。 这实际上是一个不太可能的例子,因为 [NOT] EXISTS 子查询几乎总是包含相关性。

下面看一个更加具体的例子。比如返回了存在女性员工的部门:

SELECT d.name
FROM department d
WHERE EXISTS (SELECT *FROM employee eWHERE e.gender = '女'AND e.dept_id = d.id);+-----------+
| name      |
+-----------+
| 蜀汉部    |
+-----------+

其中,EXISTS 之后是一个关联子查询,先执行外查询找到 d.dept_id;然后依次将 d.dept_id 传递给子查询,判断该部门是否存在女性员工,如果存在则返回部门信息。

NOT EXISTS 执行相反的操作。如果想要查找不存在女性员工的部门,可以将上例中的 EXISTS 替换成 NOT EXISTS。

6.横向派生表

对于派生表而言,它必须能够单独运行,而不能依赖其他表。

例如,以下语句想要返回每个部门内月薪最高的员工:

SELECT d.name, t.name, t.salary
FROM department d
LEFT JOIN (SELECT e.dept_id, e.name, e.salaryFROM employee eWHERE e.dept_id = d.idORDER BY e.salary DESCLIMIT 1) t ON d.id = t.dept_id;
ERROR 1054 (42S22): Unknown column 'd.id' in 'where clause'

该语句失败的原因在于子查询 t 不能引用外查询中的 department 表。

从 MySQL 8.0.14 开始,派生表支持 LATERAL 关键字前缀,表示允许派生表引用它所在的 FROM 子句中的其他表。这种派生表被称为横向派生表(Lateral Derived Table)。

对于上面的问题,可以使用 LATERAL 派生表实现:

SELECT d.name, t.name, t.salary
FROM department d
LEFT JOIN LATERAL (SELECT e.dept_id, e.name, e.salaryFROM employee eWHERE e.dept_id = d.idORDER BY e.salary DESCLIMIT 1) t on d.id = t.dept_id;

该语句在 LEFT JOIN 之后加上了一个 LATERAL 关键字,使得子查询 t 能够引用前面的 department 表中的字段。

如果你使用的是 MySQL 5.7 以及之前的版本,可以利用 MySQL 中的自定义变量实现相同的效果:

SELECT d.name dept_name, w.name emp_name, w.salary
FROM department d
LEFT JOIN (SELECT *FROM (SELECT a.*, IF(@did = a.dept_id, @rn := @rn+1, @rn := 1) AS rn, @did := a.dept_idFROM(SELECT * FROM employee e ORDER BY dept_id, salary DESC) a) AS tWHERE t.rn <= 1
) AS w ON d.id = w.dept_id;+-----------+----------+---------+
| dept_name | emp_name | salary  |
+-----------+----------+---------+
| 蜀汉部    | 刘备     | 1000000 |
| 曹魏部    | 曹操     | 2000000 |
| 孙吴部    | 孙权     | 1500000 |
+-----------+----------+---------+

上面的查询语句使用了自定义变量,有几处需要特别解释一下。

语句IF(@did = a.dept_id, @rn := @rn+1, @rn := 1) AS rn这是一个 IF 语句,用于计算排名。它检查当前行的部门 ID (a.dept_id) 是否与前一行的部门 ID (@did) 相同。如果相同,则排名 (@rn) 自增 1,表示同一个部门内的下一个员工。如果部门 ID 不同(即进入了新的部门),则排名 (@rn) 被重置为 1,表示这是新部门的第一个员工。AS rn 表示将计算出的排名别名为 rn,它将作为结果集的一部分返回。

@did := a.dept_id将当前行的部门 ID (a.dept_id) 赋值给用户变量 @did。该变量用在前面的 IF 语句中,用于给部门内的员工计算排名。

再给每个部门员工按照工资排序并编上部门内部排名 rn 后,将结果作为派生表 t,通过 SELECT 查询出所有部门内薪资排名第一的员工。

最后和部门表连表查询出每个部门内月薪最高的员工。

7.附录

本文示例用到的员工表(employee)和部门表(deparment)建表与数据如下。

员工表(employee):

CREATE TABLE employee(id INT UNSIGNED NOT NULL AUTO_INCREMENT,name varchar(64) NOT NULL,dept_id INT UNSIGNED,job_level INT UNSIGNED,salary INT UNSIGNED,gender CHAR(1) DEFAULT '男',PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4INSERT INTO employee(name,dept_id,job_level,salary,gender)
VALUES
('刘备', 1,1, 1000000, '男'),
('关羽', 1,2, 100000, '男'),
('张飞', 1,2, 100000, '男'),
('黄月英',1,3, 80000, '女'),
('曹操', 2,1, 2000000, '男'),
('典韦', 2,2, 200000, '男'),
('张辽', 2,2, 200000, '男'),
('孙权', 3,1, 1500000, '男'),
('吕蒙', 3,2, 150000, '男'),
('黄盖', 3,2, 150000, '男')

部门表(deparment):

CREATE TABLE department(id INT UNSIGNED NOT NULL AUTO_INCREMENT ,name varchar(64) NOT NULL,PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4INSERT INTO department(name)
VALUES
('蜀汉部'),
('曹魏部'),
('孙吴部')

参考文献

MySQL 8.0 Reference Manual :: 13.2.15 Subqueries
《MySQL 入门教程》第 19 篇 子查询 - 不剪发的Tony老师

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

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

相关文章

【建议收藏】职场人口头和书面沟通必备词语,瞬间高大上

这年头&#xff0c;在职场不但要会做&#xff0c;还要会说。 会说还不能平铺直叙的说&#xff0c;还要能把普通的工作说出话来&#xff0c;这就需要一些“考究”的用词。尤其是在某些头部企业的带领下&#xff0c;业务不够、产品不行、解决方案不够新&#xff0c;就用华丽的辞…

OAK相机:启动报错X_LINK_DEVICE_NOT_FOUND

OAK相机&#xff1a;启动报错X_LINK_DEVICE_NOT_FOUND 环境报错原因与解决未设置 udev 规则USB崩溃排线接触不良或相机模块时钟干扰 环境 硬件&#xff1a; 4✖️OV9782相机模组OAK-FFC-4P驱动模组笔记本电脑 软件&#xff1a; Ubuntu18.04python 3.9depthai 2.21.2.0 报错…

ASEMI二极管1N4148(T4)的用途和使用建议

编辑-Z 二极管是一种常见的电子元件&#xff0c;其中1N4148&#xff08;T4&#xff09;是一款广泛使用的快恢复二极管。它具有快速的开关特性和高反向阻挡能力&#xff0c;适用于多种电子应用。本文将介绍1N4148&#xff08;T4&#xff09;的特点、用途和如何正确使用该二极管…

如何使用极狐GitLab 支持 ISO 27001 合规

目录 组织控制 技术控制 了解更多 本文来源&#xff1a;about.gitlab.com 作者&#xff1a;Joseph Longo 译者&#xff1a;武让 极狐GitLab 高级解决方案架构师 作为一体化平台&#xff0c;通过极狐GitLab 可以很容易实现 DevSecOps 全生命周期管理。极狐GitLab 使开发人员能…

无涯教程-JavaScript - FALSE函数

描述 FALSE函数返回逻辑值FALSE。 语法 FALSE () 争论 FALSE函数没有参数。 Notes 您还可以在工作表或公式中直接键入FALSE单词,Microsoft Excel会将其解释为逻辑值FALSE。 提供FALSE功能主要是为了与其他电子表格程序兼容。 适用性 Excel 2007,Excel 2010,Excel 2013…

webhook--详解(gitee 推送)

一、简介 webhook 是一种基于 HTTP 的回调函数&#xff0c;可在 2 个应用编程接口&#xff08;API&#xff09;之间实现轻量级的事件驱动通信。是一种新型的前后端交互方式&#xff0c;一种对客户端-服务器模式的逆转&#xff0c;在传统方法中&#xff0c;客户端从服务器请求数…

怎样做一个简易而温馨的原木风居室空间

由 balbek bureau 设计的 Relogged 是一座重新设计的私人住宅&#xff0c;位于乌克兰河岸的绿化区。顾名思义&#xff0c;该项目重新诠释了木屋的概念&#xff0c;并与充满自然气息的环境相呼应&#xff0c;营造出宁静舒适的生活氛围。在探索重新设计的木屋实例时&#xff0c;建…

设计模式:享元模式

设计模式&#xff1a;享元模式 什么是享元模式 首先我们需要简单了解一下什么是享元模式。享元模式(Flyweight Pattern):主要用于减少创建对象的数量&#xff0c;以减少内存占用和提高性能。享元模式的重点就在这个享字&#xff0c;通过一些共享技术来减少对象的创建&#xff…

java---jar详解

一、help C:\Users\lichf1>jar 用法: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ... 选项:-c 创建新档案-t 列出档案目录-x 从档案中提取指定的 (或所有) 文件-u 更新现有档案-v 在标准输出中生成详细输出-f 指定档案文件名-m…

《Web安全基础》04. 文件操作安全

web 1&#xff1a;文件操作安全2&#xff1a;文件上传漏洞2.1&#xff1a;简介2.2&#xff1a;防护与绕过2.3&#xff1a;WAF 绕过2.3.1&#xff1a;数据溢出2.3.2&#xff1a;符号变异2.3.3&#xff1a;数据截断2.3.4&#xff1a;重复数据 3&#xff1a;文件包含漏洞4&#xf…

7、Spring之依赖注入源码解析(下)

resolveDependency()实现 该方法表示,传入一个依赖描述(DependencyDescriptor),该方法会根据该依赖描述从BeanFactory中找出对应的唯一的一个Bean对象。 @Nullable Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Null…

一个最简verilog代码的分析

module testmod( input CLK, output reg [1:0]acc);always(posedge CLK)acc<acc2d1; endmodule 上述代码综合后的电路图为&#xff1a; 分析1 假设在t1时刻&#xff0c;两个触发器的状态都是1&#xff0c;即acc2b11&#xff0c;此时半加器1的A端是1&#xff0c;则D触发器1…

Postman接口压力测试 ---- Tests使用(断言)

所谓断言&#xff0c;主要用于测试返回的数据结果进行匹配判断&#xff0c;匹配成功返回PASS&#xff0c;失败返回FAIL。 下图方法一&#xff0c;直接点击右侧例子函数&#xff0c;会自动生成出现在左侧窗口脚本&#xff0c;只需修改数据即可。 方法二&#xff1a;直接自己写脚…

动态封装对象,属性来自json

需求&#xff1a; 如何动态的获取一个对象的字段&#xff0c;假如一个对象里面有name,age&#xff0c;sex三个字段&#xff0c;我想取name的值&#xff0c;这个name是存在一个json中&#xff0c;json的格式如下[{"key":"name"},{"key":"age…

使用内网端口映射方案,轻松实现U8用友ERP的本地部署异地远程访问——“cpolar内网穿透”

文章目录 前言1. 服务器本机安装U8并调试设置2. 用友U8借助cpolar实现企业远程办公2.1 在被控端电脑上&#xff0c;点击开始菜单栏&#xff0c;打开设置——系统2.2 找到远程桌面2.3 启用远程桌面 3. 安装cpolar内网穿透3.1 注册cpolar账号3.2 下载cpolar客户端 4. 获取远程桌面…

排序算法概述

1、数据排序&#xff1a; 将一个文件的记录按关键字不减&#xff08;或不增&#xff09;次序排列&#xff0c;使文件成为有序文件&#xff0c;此过程称为排序。 2、排序的稳定性&#xff1a; 稳定排序&#xff1a; 若排序后&#xff0c;相同关键字的记录保持它们原来的相对次序…

机器学习笔记之最优化理论与方法(九)无约束优化问题——常用求解方法(下)

机器学习笔记之最优化理论与方法——基于无约束优化问题的常用求解方法[下] 引言回顾&#xff1a;经典牛顿法的缺陷与拟牛顿法思想经典牛顿法缺陷与修正牛顿法拟牛顿法与矩阵 B k 1 \mathcal B_{k1} Bk1​的选择 拟牛顿法之 DFP \text{DFP} DFP方法 DFP \text{DFP} DFP迭代公式…

探索装饰艺术的未来,留存传统的精髓

近一个世纪后&#xff0c;装饰艺术终于卷土重来。正如我们在全球新的项目、室内空间和家具中所看到的&#xff0c;那种令我们渴望20世纪初20年代繁荣时期的奢华和魅力。作为装饰艺术建筑和设计的独特身份一直在世界上继续启发着人们&#xff0c;那么从新的设计和现有设计的保留…

从零基础到精通Flutter开发:一步步打造跨平台应用

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 导言 Flutter是一种流行…

【RocketMQ】消息的拉取

在上一讲中&#xff0c;介绍了消息的存储&#xff0c;生产者向Broker发送消息之后&#xff0c;数据会写入到CommitLog中&#xff0c;这一讲&#xff0c;就来看一下消费者是如何从Broker拉取消息的。 RocketMQ消息的消费以组为单位&#xff0c;有两种消费模式&#xff1a; 广播…