当前位置: 首页 > news >正文

SQL基础⑦ | 子查询

0 序言

本文将系统讲解SQL中子查询的概念分类执行逻辑实际用法,包括单行子查询多行子查询相关子查询的操作符与示例,以及子查询的注意事项。

通过学习,你将掌握如何使用子查询解决复杂查询问题,理解不同类型子查询的适用场景,提升SQL查询的灵活性与效率。

1 子查询概述

1.1 什么是子查询

什么是子查询呢?
子查询是嵌套在另一个查询语句内部的查询。

从MySQL 4.1开始引入,它能增强SELECT查询能力,支持从结果集获取数据,或用一个查询结果作为另一个查询的条件,

打个例子,如“谁的工资比TOM高”可通过子查询先获取Abel的工资,再筛选更高工资的员工。

这样子查询的效率就提高了。

1.2 基本语法

SELECT select_list
FROM table
WHERE expr operator (SELECT select_listFROM table
);
  • 子查询需包含在括号内,通常放在比较条件右侧。
  • 子查询(内查询)先执行,结果供主查询(外查询)使用。

1.3 分类

1.3.1 按结果行数分类

  • 单行子查询:内查询返回一条记录,对应单行比较操作符(=、>、<等)。
  • 多行子查询:内查询返回多条记录,对应多行比较操作符(IN、ANY、ALL等)。

1.3.2 按执行次数分类

  • 不相关子查询:内查询仅执行一次,结果作为主查询条件。比方说先查A的工资,再用该值筛选其他员工)。
  • 相关子查询:内查询执行次数依赖外部查询,每次外部查询行变化时,子查询需重新计算。比如查询员工工资高于本部门平均工资)。

2 单行子查询

2.1 单行比较操作符

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

2.2 代码示例

2.2.1 子查询基础

查询工资大于149号员工工资的员工姓名

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

说明:子查询先获取149号员工的工资,主查询用该值筛选工资更高的员工。

在这里插入图片描述

2.2.2 多条件子查询

返回job_id与141号员工相同、工资比142号员工高的员工信息

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 = 142
);

在这里插入图片描述

2.2.3 在HAVING中使用子查询

查询最低工资大于50号部门最低工资的部门ID及最低工资

SELECT department_id, MIN(salary) 
FROM employees 
GROUP BY department_id 
HAVING MIN(salary) > (SELECT MIN(salary) FROM employees WHERE department_id = 50
);

结果如下:

在这里插入图片描述

2.3 注意事项

  • 子查询返回多行时,使用单行操作符会报错!!!
  • 子查询结果为空时,主查询可能返回空集(如查询与“Haas”同job_id的员工,若“Haas”不存在则无结果)。

3 多行子查询

3.1 多行比较操作符

操作符含义
IN等于列表中的任意一个值
ANY与单行操作符配合,和子查询返回的某一个值比较
ALL与单行操作符配合,和子查询返回的所有值比较
SOMEANY的别名,作用相同

3.2 代码示例

3.2.1 使用ANY

返回其他job_id中比IT_PROG部门任一工资低的员工信息

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

在这里插入图片描述

3.2.2 使用ALL

返回其他job_id中比“IT_PROG”部门所有工资都低的员工信息

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';

在这里插入图片描述

这里需要明白两者之间的区别,

ANY是存在一个即可,ALL是全部满足才行,两者筛选范围不同,导致结果集大小有差异,

通常来说,ANY的结果要多于ALL。

3.2.3 查询平均工资最低的部门ID

-- 方式1:嵌套子查询
SELECT department_id 
FROM employees 
GROUP BY department_id 
HAVING AVG(salary) = (SELECT MIN(avg_sal) FROM (SELECT AVG(salary) avg_sal FROM employees GROUP BY department_id) dept_avg_sal
);-- 方式2:使用ALL
SELECT department_id 
FROM employees 
GROUP BY department_id 
HAVING AVG(salary) <= ALL (SELECT AVG(salary) FROM employees GROUP BY department_id
);

在这里插入图片描述

实现的功能都一样,就是写法不同,

两种的方法

3.3 注意事项

这里主要说明的点在于空值问题。

  • 空值问题:若子查询返回包含NULL的值,可能导致结果异常。

例如

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

因manager_id可能为NULL,NOT IN会判定为未知,返回空集。

就是这么一个情况。

4 相关子查询

4.1 执行流程

相关子查询的执行依赖外部查询,需循环执行:

  1. 从主查询获取一行数据;
  2. 子查询使用主查询的当前行数据进行计算;
  3. 若满足条件,则主查询保留该行;
  4. 重复上述步骤,直到主查询所有行处理完毕。

4.2 代码示例

4.2.1 基础示例

查询工资大于本部门平均工资的员工信息

USE demo_hr;
SELECT last_name, salary, department_id 
FROM employees AS outer_tbl 
WHERE salary > (SELECT AVG(salary) FROM employees WHERE department_id = outer_tbl.department_id
);

(注:子查询中outer.department_id引用主查询的当前部门ID,每次主查询行变化时重新计算部门平均工资)

在这里插入图片描述

4.2.2 使用EXISTS

查询公司管理者的信息(存在下属的员工)

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
);

要注意哦,

EXISTS仅判断子查询是否有结果,有则保留主查询行,它的效率是高于IN的!

在这里插入图片描述

4.2.3 相关更新与删除

  • 相关更新:依据另一表数据更新当前表

    -- 为employees表增加department_name字段并填充部门名称
    ALTER TABLE employees ADD (department_name VARCHAR(14));
    UPDATE employees e 
    SET department_name = (SELECT department_name FROM departments d WHERE e.department_id = d.department_id
    );
    
  • 相关删除:依据另一表数据删除当前表记录

    -- 删除与emp_history表中重复的员工数据
    DELETE FROM employees e 
    WHERE employee_id IN (SELECT employee_id FROM emp_history WHERE employee_id = e.employee_id
    );
    

5 子查询与自连接的对比

查询工资比Abel高的员工,可用子查询或自连接:

-- 子查询
SELECT last_name, salary 
FROM employees 
WHERE salary > (SELECT salary FROM employees WHERE last_name = 'Abel'
);-- 自连接
SELECT e2.last_name, e2.salary 
FROM employees e1, employees e2 
WHERE e1.last_name = 'Abel' 
AND e1.salary < e2.salary;

这两个运行的结果都一样,从直观观感来说,

自连接的程序会更简洁一些,

但这个因人而异,熟练了其实都差不多。

但大多数来讲,自连接更高效,多数数据库对自连接优化更好,

因为自连接基于已知表关联,而子查询需先处理未知结果集,执行速度较慢。

6 总结

子查询是嵌套在其他查询中的查询,按结果行数可分为单行多行两种。

其中单行用=、>等,多行用IN、ANY

按执行次数可分为不相关(一次执行)相关(依赖外部查询,循环执行)

我们在使用时要注意操作符与子查询类型匹配、空值对结果的影响,以及EXISTS的高效性。

实际开发中,

如果你遇到的是比较复杂查询可优先考虑自连接

因为执行速度会更快,小型数据库里看不出太大差距,

但是数据库一庞大起来的话,自连接的优势就会体现出来啦!

子查询则适用于逻辑更清晰的场景。

就讲这么多了。

http://www.dtcms.com/a/294800.html

相关文章:

  • Linux——System V 共享内存 IPC
  • 【第十二章 W55MH32 NetBIOS示例标题】
  • ChatGPT桌面版深度解析
  • clientHeight(用于获取元素的可视高度)
  • 大致自定义文件I/O库函数的实现详解(了解即可)
  • 计算机网络学习----域名解析
  • uni-app平板端自定义样式合集
  • 【67】MFC入门到精通——MFC 销售管理系统 项目实现详细教程
  • 【自动化运维神器Ansible】深入解析Ansible Host-Pattern:精准控制目标主机的艺术
  • PowerShell自动化核对AD与HR系统账户信息实战指南
  • Hexo - 免费搭建个人博客02 - 创建个人博客
  • 智能办公如何创建e9流程
  • 力扣刷题(第九十六天)
  • Windows 用 Python3 快速搭建 HTTP 服务器
  • Google Chrome V8< 14.0.221 类型混淆漏洞
  • 基于Kafka实现动态监听topic功能
  • 元图CAD:高效分割图纸的智能解决方案
  • CSP-J系列【2024】P11230 [CSP-J 2024] 接龙题解
  • 数据持久化--PlayerPrefs
  • GRE实验
  • ROS是什么?
  • 力扣面试150(39/150)
  • PyTorch中的词嵌入层(nn.Embedding)详解与实践指南
  • js生成器
  • 【黑产大数据】2025年上半年互联网黑灰产趋势年度总结
  • MySQL 8.0 OCP 1Z0-908 题目解析(33)
  • 【硬件】Fan in和Fan out
  • 《地牢探险者:深渊回响》(C++游戏,爆肝7.8个小时,全文14591字)(求一个免费的三连)
  • c# sqlsuger 获取关联表中一个值
  • SET: Spectral Enhancement for Tiny Object Detection(CVPR2025)