【重学 MySQL】四十四、相关子查询

news2024/11/20 14:42:16

【重学 MySQL】四十四、相关子查询

  • 相关子查询执行流程
  • 示例
    • 使用相关子查询进行过滤
    • 使用相关子查询进行存在性检查
    • 使用相关子查询进行计算
  • 在 `select`,`from`,`where`,`having`,`order by` 中使用相关子查询举例
    • `SELECT` 子句中使用相关子查询
    • `FROM` 子句中使用相关子查询
    • `WHERE` 子句中使用相关子查询
    • `HAVING` 子句中使用相关子查询
    • `ORDER BY` 子句中使用相关子查询
    • 总结
  • `EXISTS` 和 `NOT EXISTS`
    • `EXISTS`
    • `NOT EXISTS`
    • 关键点
  • 注意事项
  • 替代方法

在这里插入图片描述
在 MySQL 中,相关子查询(也称为相关子查询或关联子查询)是一种特殊类型的子查询,其执行依赖于外部查询的当前行值。这意味着相关子查询在外部查询的每一行上都会重新执行一次,并且可以使用外部查询的列值。

相关子查询执行流程

相关子查询的执行流程涉及多个步骤,并且这些步骤在数据库管理系统(DBMS)中是高度优化的。

  1. 解析和优化

    • 数据库管理系统首先解析SQL语句,包括相关子查询,以确保其符合语法规则。
    • 接着,系统进行语义解析,检查表名、列名、数据类型、权限等约束条件是否满足。
    • 对于包含相关子查询的查询语句,DBMS会尝试找到最优的查询计划,以便快速地从数据库中检索所需的数据。这包括选择最佳的索引、使用缓存和预处理语句等优化措施。
  2. 生成执行计划

    • 在查询优化后,系统会生成一个执行计划,该计划描述了如何获取查询结果,包括访问哪些表、采用哪些索引、如何连接各个表等。
    • 对于相关子查询,执行计划会考虑子查询与外部查询之间的依赖关系,并确定子查询的执行时机和方式。
  3. 执行外部查询

    • 外部查询(即包含相关子查询的查询)开始执行。在外部查询的每一行处理过程中,都会涉及到相关子查询的执行。
  4. 执行相关子查询

    • 对于外部查询中的每一行,DBMS都会执行一次相关子查询。
    • 相关子查询依赖于外部查询的当前行值。这意味着,每次外部查询处理一行数据时,子查询都会使用该行数据中的值作为条件来执行。
    • 子查询的结果通常用于过滤、排序或作为外部查询的一部分进行计算。
  5. 组合结果

    • 外部查询根据子查询的结果来处理每一行数据,并生成最终的查询结果集。
    • 如果子查询返回多个结果,外部查询可能会使用这些结果来进行进一步的过滤或计算。
  6. 返回结果

    • 最后,数据库将查询结果集返回给客户端应用程序。

需要注意的是,相关子查询可能会导致性能问题,因为对于外部查询返回的每一行数据,数据库都需要重新执行子查询。因此,在编写包含相关子查询的SQL语句时,应谨慎考虑其性能影响,并尝试使用其他优化技术(如索引、连接优化、窗口函数等)来提高查询效率。

此外,虽然相关子查询在某些情况下非常有用,但在其他情况下,使用连接(JOIN)操作或窗口函数可能更加高效和直观。因此,在选择使用哪种查询技术时,应根据具体需求和性能考虑做出决策。

示例

使用相关子查询进行过滤

假设我们有两个表:employees(员工)和 departments(部门)。我们想要找到每个部门中工资最高的员工。

SELECT e.name, e.salary, e.department_id
FROM employees e
WHERE e.salary = (
    SELECT MAX(sub.salary)
    FROM employees sub
    WHERE sub.department_id = e.department_id
);

在这个查询中,子查询 SELECT MAX(sub.salary) FROM employees sub WHERE sub.department_id = e.department_id 是一个相关子查询,因为它依赖于外部查询的 e.department_id

使用相关子查询进行存在性检查

假设我们有两个表:students(学生)和 courses(课程)。我们想要找到那些选修了所有课程的学生。

SELECT s.name
FROM students s
WHERE NOT EXISTS (
    SELECT c.course_id
    FROM courses c
    WHERE NOT EXISTS (
        SELECT 1
        FROM enrollments e
        WHERE e.student_id = s.student_id AND e.course_id = c.course_id
    )
);

在这个查询中,内部子查询 SELECT 1 FROM enrollments e WHERE e.student_id = s.student_id AND e.course_id = c.course_id 是一个相关子查询,它依赖于外部子查询的 c.course_id 和外部查询的 s.student_id

使用相关子查询进行计算

假设我们有一个表 sales,其中包含每个销售员的销售记录。我们想要计算每个销售员的销售总额,并找出销售额超过该销售员平均销售额的记录。

SELECT s.salesperson_id, s.sale_amount
FROM sales s
WHERE s.sale_amount > (
    SELECT AVG(sub.sale_amount)
    FROM sales sub
    WHERE sub.salesperson_id = s.salesperson_id
);

在这个查询中,子查询 SELECT AVG(sub.sale_amount) FROM sales sub WHERE sub.salesperson_id = s.salesperson_id 是一个相关子查询,因为它依赖于外部查询的 s.salesperson_id

selectfromwherehavingorder by 中使用相关子查询举例

在SQL查询中,相关子查询(也称为相关子选择或相关嵌套查询)是指依赖于外部查询中的值的子查询。它们通常用于在SELECTFROMWHEREHAVINGORDER BY子句中实现复杂的逻辑。以下是一些示例,展示了如何在这些子句中使用相关子查询。

SELECT 子句中使用相关子查询

虽然直接在SELECT子句中使用相关子查询不太常见,但你可以通过派生表(子查询作为表)间接实现。不过,这里展示一个更直接的场景,即在SELECT中嵌入相关子查询作为计算列。

SELECT 
    employee_id,
    first_name,
    last_name,
    (SELECT COUNT(*) 
     FROM orders 
     WHERE orders.employee_id = employees.employee_id) AS order_count
FROM 
    employees;

这个查询为每个员工返回了一个订单计数。

FROM 子句中使用相关子查询

FROM子句中使用相关子查询通常通过派生表(子查询作为临时表)来实现,但相关子查询在这种场景下不常见。然而,你可以通过JOINWHERE条件实现类似的效果。

select e.last_name, e.salary, e.department_id
from employees e, (select department_id, avg(salary) avg_salary
      from employees
      group by department_id) t_dept_avg_salary
where e.department_id   = t_dept_avg_salary.department_id
 and e.salary > t_dept_avg_salary.avg_salary;

它使用了隐式内连接(也称为笛卡尔积加过滤)来比较每个员工的工资与其所在部门的平均工资。这里,您创建了一个派生表(也称为子查询或临时表)t_dept_avg_salary,该表包含了每个部门的平均工资。然后,您将这个派生表与employees表连接起来,以便比较每个员工的工资与其部门的平均工资。

  1. 派生表 t_dept_avg_salary

    (select department_id, avg(salary) avg_salary
     from employees
     group by department_id)
    

    这个子查询从employees表中计算每个部门的平均工资,并将结果作为一个临时表(派生表)。这个表有两列:department_id(部门ID)和avg_salary(该部门的平均工资)。

  2. 主查询

    select e.last_name, e.salary, e.department_id
    from employees e, (子查询) t_dept_avg_salary
    where e.department_id = t_dept_avg_salary.department_id
      and e.salary > t_dept_avg_salary.avg_salary;
    

    主查询从employees表(别名为e)和派生表t_dept_avg_salary中选择数据。它通过department_id将这两个表连接起来,并过滤出那些工资高于其部门平均工资的员工。

  3. 结果
    查询结果将包含那些工资高于其所在部门平均工资的员工的姓氏(last_name)、工资(salary)和部门ID(department_id)。

虽然您的查询在功能上是正确的,但现代SQL风格通常推荐使用显式的JOIN语法来替代隐式连接,因为它更清晰且更易于维护。以下是使用显式JOIN的等效查询:

SELECT e.last_name, e.salary, e.department_id
FROM employees e
JOIN (
    SELECT department_id, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department_id
) t_dept_avg_salary ON e.department_id = t_dept_avg_salary.department_id
WHERE e.salary > t_dept_avg_salary.avg_salary;

这个查询与您的原始查询在逻辑上是相同的,但使用了显式的JOIN语法,这通常被认为是更好的做法。

WHERE 子句中使用相关子查询

WHERE子句中使用相关子查询非常常见,用于过滤记录。

SELECT 
    employee_id,
    first_name,
    last_name
FROM 
    employees e
WHERE 
    (SELECT COUNT(*) 
     FROM orders o 
     WHERE o.employee_id = e.employee_id) > 5;

这个查询返回了订单数量超过5的员工。

HAVING 子句中使用相关子查询

HAVING子句通常用于聚合查询的过滤,但在HAVING中使用相关子查询的情况较少。这里通过一个例子展示如何在HAVING中嵌入相关子查询。

SELECT 
    department_id,
    COUNT(employee_id) AS employee_count
FROM 
    employees
GROUP BY 
    department_id
HAVING 
    COUNT(employee_id) > (SELECT AVG(emp_count) 
                          FROM (SELECT COUNT(employee_id) AS emp_count 
                                FROM employees 
                                GROUP BY department_id) AS avg_dept_counts);

这个查询返回了员工数量超过所有部门平均员工数量的部门。

ORDER BY 子句中使用相关子查询

ORDER BY子句中使用相关子查询的情况也不常见,但可以通过派生表或窗口函数实现类似效果。不过,直接嵌入相关子查询也可以在某些特殊情况下使用。

SELECT 
    employee_id,
    first_name,
    last_name,
    salary
FROM 
    employees
ORDER BY 
    (SELECT AVG(salary) 
     FROM employees 
     WHERE department_id = employees.department_id) DESC,
    salary DESC;

这个查询首先按部门平均工资降序排序,然后按员工个人工资降序排序。

总结

相关子查询在SQL查询中非常强大,可以用于实现复杂的逻辑。然而,它们可能会降低查询性能,特别是在处理大量数据时。因此,在使用相关子查询时,应考虑其性能影响,并考虑使用其他优化技术,如索引、连接优化或窗口函数等。

EXISTSNOT EXISTS

EXISTSNOT EXISTS 是 SQL 中用于测试子查询是否返回任何行的条件运算符。它们通常用于在 WHERE 子句或 HAVING 子句中,以确定是否满足某个条件,从而决定是否包含某些行在结果集中。

EXISTS

EXISTS 运算符用于测试子查询是否返回至少一行。如果子查询返回一行或多行,EXISTS 条件就为真(TRUE),否则为假(FALSE)。

示例

SELECT first_name, last_name
FROM employees e
WHERE EXISTS (
    SELECT 1
    FROM departments d
    WHERE e.department_id = d.department_id
    AND d.department_name = 'Sales'
);

这个查询返回了所有在名为 ‘Sales’ 的部门工作的员工的名字。子查询检查是否存在至少一个部门,其 department_idemployees 表中的 department_id 匹配,并且部门名称为 ‘Sales’。

NOT EXISTS

NOT EXISTS 运算符用于测试子查询是否不返回任何行。如果子查询没有返回任何行,NOT EXISTS 条件就为真(TRUE),否则为假(FALSE)。

示例

SELECT first_name, last_name
FROM employees e
WHERE NOT EXISTS (
    SELECT 1
    FROM departments d
    WHERE e.department_id = d.department_id
    AND d.department_name = 'HR'
);

这个查询返回了所有不在名为 ‘HR’ 的部门工作的员工的名字。子查询检查是否不存在任何部门,其 department_idemployees 表中的 department_id 匹配,并且部门名称为 ‘HR’。

关键点

  • EXISTSNOT EXISTS 子查询通常只关心是否存在行,而不关心行的具体内容。因此,子查询中的 SELECT 子句经常简单地选择常量(如 SELECT 1),因为实际选择的列并不重要。
  • 这些运算符通常比使用 INNOT INJOIN(在某些情况下)等替代方法更高效,特别是当子查询可能返回大量行时。
  • 使用 EXISTSNOT EXISTS 时,应确保子查询中的条件能够正确地反映你想要测试的逻辑。
  • 在某些数据库系统中,EXISTSNOT EXISTS 可能会利用索引来优化查询性能。因此,在设计数据库和编写查询时,考虑索引的使用是很重要的。

注意事项

  1. 性能问题:由于相关子查询在外部查询的每一行上都会重新执行,因此可能会导致性能问题,特别是在处理大数据集时。在这种情况下,可以考虑使用 JOIN 或其他优化技术。

  2. 可读性:相关子查询有时可能使查询变得难以理解和维护。因此,在编写复杂查询时,确保代码清晰并添加适当的注释。

  3. 索引:确保在相关子查询中使用的列上建立适当的索引,以提高查询性能。

替代方法

在某些情况下,可以使用 JOIN 或窗口函数(MySQL 8.0+ 支持)来替代相关子查询,从而获得更好的性能和可读性。例如,上面的第一个示例(找到每个部门中工资最高的员工)可以使用 JOIN 和 GROUP BY 来重写:

SELECT e1.name, e1.salary, e1.department_id
FROM employees e1
JOIN (
    SELECT department_id, MAX(salary) AS max_salary
    FROM employees
    GROUP BY department_id
) e2 ON e1.department_id = e2.department_id AND e1.salary = e2.max_salary;

这种重写方式通常更高效,因为它避免了相关子查询的重复执行。

通过理解和使用相关子查询,你可以解决一些复杂的查询问题。然而,要注意性能问题,并考虑使用其他技术来优化查询。

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

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

相关文章

带你0到1之QT编程:二十二、QChart类图表及折线图、直方图、饼图的三大可视化图表实战!

此为QT编程的第二十二谈!关注我,带你快速学习QT编程的学习路线! 每一篇的技术点都是很很重要!很重要!很重要!但不冗余! 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点!…

09_OpenCV彩色图片直方图

import cv2 import numpy as np import matplotlib.pyplot as plt %matplotlib inlineimg cv2.imread(computer.jpeg, 1) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) plt.imshow(img) plt.show()plot绘制直方图 plt.hist(img.ravel(), 256) #ravel() 二维降一维 256灰度级…

【JavaEE】http/https 超级详解

🔥个人主页: 中草药 🔥专栏:【Java】登神长阶 史诗般的Java成神之路 🦊一.定义 HTTP(HyperText Transfer Protocol)即超文本传输协议,他是应用非常广泛的应用层协议,是…

《PMI-PBA认证与商业分析实战精析》 第3章 需要评估

本章涵盖的考试重点: 需要评估的四项活动 需要评估四项活动的可交付成果 需要评估相关活动的技术 商业论证的内容 情境说明书的格式 目的、目标和商业论证的层次结构 成本收益分析的四种财务计价方法 需要评估领域就是聚焦在目标定义上。 商业分析师所需要…

ZenStack全栈开发工具(一)快速使用指南

简介 ZenStack是一个TypeScript工具,通过灵活的授权和自动生成的类型安全的 API/钩子来增强 Prisma ORM,从而简化全栈开发 数据库-》应用接口 数据库-》前端 参考官方网站:https://zenstack.dev/ 如果我们想做一个全栈开发的web应用程序&am…

目标检测技术的发展:从R-CNN、YOLO到DETR、DINO

“深度人工智能”是成都深度智谷科技旗下的人工智能教育机构订阅号,主要分享人工智能的基础知识、技术发展、学习经验等。此外,订阅号还为大家提供了人工智能的培训学习服务和人工智能证书的报考服务,欢迎大家前来咨询,实现自己的…

[FlareOn3]Challenge11

载入PE. 32 bit,无壳. 载入IDA(32bit). 寻找main函数. int __cdecl main(int argc, const char **argv, const char **envp) {char Buffer[128]; // [esp0h] [ebp-94h] BYREFchar *Str1; // [esp80h] [ebp-14h]char *Str2; // [esp84h] [eb…

ROS理论与实践学习笔记——2 ROS通信机制之常用API

"API" 是 "Application Programming Interface" 的缩写,指的是应用程序编程接口。API是一组定义了不同软件组件如何互相通信的规范。它允许不同的软件系统之间共享功能,提供一种标准的方式来访问某个软件组件的功能或数据。 详细内…

JavaScript模块化-CommonJS规范和ESM规范

1 ES6模块化 1.1 ES6基本介绍 ES6 模块是 ECMAScript 2015(ES6)引入的标准模块系统,广泛应用于浏览器环境下的前端开发。Node.js环境主要使用CommonJS规范。ESM使用import和export来实现模块化开发从而解决了以下问题: 全局作用…

《安富莱嵌入式周报》第343期:雷电USB4开源示波器正式发布,卓越的模拟前端低噪便携示波器,自带100W电源的便携智能烙铁,NASA航空航天锂电池设计

周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 更新一期视频教程 【授人以渔】CMSIS-RTOS V2封装层专题视频,一期视频将常用配置和用法梳理清楚&#xff0…

Win10系统插入带有麦克风的耳机_麦克风不起作用_解决方法_亲测成功---Windows运维工作笔记054

今天我在使用讯飞输入法的时候,想通过讯飞的语音输入法来提高自己的输入效率。 但是这个时候发现一个问题就是我插入我的台式机的是一个带有麦克风的耳机。 但是发现我这个耳机没有办法被电脑识别出麦克风来,所以说就没办法使用讯飞输入法的语音输入功能来直接输入文字了。…

Qt 窗口中鼠标点击事件的坐标探讨

// 鼠标点击事件 void Widget::mousePressEvent(QMouseEvent *event) {/*event->pos()、event->windowPos()和event->localPos()都表示鼠标点击位置在窗口中的位置,它们的值都是一样的,区别在于event->pos()是QPoint类型,event-&…

操作系统-磁盘管理

存储管理中的磁盘管理涉及到几个核心概念:磁道、扇区、磁头、盘面。 磁道:磁盘表面的同心圆,用于记录数据。每个磁道可以存储相同量的信息。 扇区:磁道被进一步划分的更小单元,通常是磁道的最小存储单位。一个常见的扇…

【新闻转载】Storm-0501:勒索软件攻击扩展到混合云环境

icrosoft发出警告,勒索软件团伙Storm-0501近期调整了攻击策略,目前正将目标瞄准混合云环境,旨在全面破坏受害者的资产。 该威胁行为者自2021年首次露面,起初作为Sabbath勒索软件行动的分支。随后,他们开始分发来自Hive…

C++发邮件:如何轻松实现邮件自动化发送?

C发邮件的详细步骤与教程指南?如何在C中发邮件? 无论是定期发送报告、通知客户还是管理内部沟通,自动化邮件系统都能显著提升工作效率。AokSend将详细介绍如何使用C发邮件,实现邮件自动化发送,帮助您轻松管理邮件通信…

10/1 力扣 49.字母异位词分组

基本知识: 关于字符串的排序: 1.多个字符串排序 1.1使用python内置的sorted() 使用该函数后原对象并不发生变化 1.2若多个字符串使用列表进行存储,使用列表的sort()方法 使用该函数后原对象原地变化 2.对单个字符串里的字母进行排序 使…

关于CSS 案例_新闻内容展示

新闻要求 标题:居中加粗发布日期: 右对齐分割线: 提示, 可以使用 hr 标签正文/段落: 左侧缩进插图: 居中显示 展示效果 审核过不了&#xff0c;内容没填大家将就着看吧。 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset&qu…

【异常数据检测】孤立森林算法异常数据检测算法(数据可视化 Matlab语言)

摘要 本文研究了基于孤立森林算法的异常数据检测方法&#xff0c;并在MATLAB中实现了该算法的可视化。孤立森林是一种无监督的异常检测算法&#xff0c;主要通过构建决策树来区分正常数据和异常数据。本文使用真实数据集&#xff0c;通过二维可视化展示了检测结果。实验结果表…

肝郁气滞有什么症状

在这个快节奏、高压力的时代&#xff0c;我们的身体往往承载着超负荷的情绪与压力&#xff0c;而“肝郁气滞”这一中医术语&#xff0c;正悄然成为许多现代人健康的隐形杀手。它如同体内的“情绪交通堵塞”&#xff0c;不仅影响心情&#xff0c;更波及全身健康。今天&#xff0…

计算机毕业设计 基于Python的新闻采集与订阅平台的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…