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

【SQL server】 SQL子查询:与连接的区别、类型划分、相关与非相关子查询对比

文章目录

  • 前言
  • 一、子查询(Subquery) VS 连接(Join)
    • 1.1子查询与连接的比较
    • 1.2 什么时候用子查询
    • 1.3 小结
  • 二、常见的子查询
    • 2.1 标量子查询(Scalar Subquery)
    • 2.2 列子查询(Column Subquery)
    • 2.3 表子查询(Table Subquery)
  • 三、相关子查询(Correlated Subquery)与非相关子查询(Non-Correlated Subquery)
    • 3.1 非相关子查询
    • 3.2 相关子查询
  • 总结


前言

子查询是一个嵌套在 SELECT、INSERT、UPDATE 或 DELETE 语句或其他子查询中的查询。将一个查询的结果作为另一个查询的条件或数据源,实现对数据的多层筛选和处理。
子查询也称为内部查询或内部选择,而包含子查询的语句也称为外部查询或外部选择。


一、子查询(Subquery) VS 连接(Join)

1.1子查询与连接的比较

子查询常用于解决一些无法通过单一查询直接完成的复杂数据检索任务。例如,当需要获取与特定条件相关联的数据,或者从一个表中获取满足另一个表中某些条件的数据时,子查询就能够发挥重要作用。它可以替代一些复杂的连接操作,在某些情况下使查询逻辑更加清晰,也能帮助实现数据的分层分析和过滤。所以,其实许多包含子查询的Transact-SQL语句也可以通过联接来表示,并且在某些必须检查存在性的情况下,连接会产生更好的性能。

本文实例ER 图
在这里插入图片描述

下面分别通过获取学生班级的T-SQL比较下子查询和连接实现起来异同
子查询

select T_Student.Name,T_Student.Age,(SELECT ClassName FROM T_Class WHERE Id = T_Student.ClassId) AS ClassName
from T_Student
where T_Student.ClassId in (select T_Class.Id from T_Classwhere T_Class.ClassName = '计算机科学与技术2024级1班'
)

连接

select T_Student.Name,T_Student.Age,T_Class.ClassName
from T_Student
inner join T_Class on T_Class.Id = T_Student.ClassId
where T_Class.ClassName = '计算机科学与技术2024级1班'

执行结果
在这里插入图片描述
通过观察执行结果,两种写法在功能上完全等价。但是对于子查询而言,先执行子查询获取班级 ID,再筛选学生,最终在显示班级名称的时候又用到了关联子查询查询班级名称。在某些情况下,子查询可能导致多次查询,而连接查询可通过一次扫描完成,因此效率可能更高。但实际性能需结合执行计划判断。

1.2 什么时候用子查询

子查询可能比连接查询更具优势,主要体现在以下几个方面

  1. 逻辑分层更清晰,可读性更强
  2. 处理存在性检查更简洁
  3. 避免多表连接的冗余数据
  4. 支持更灵活的标量计算

举个例子,我们查询学生课程里,每门课程绩在80分以上的同学。分别于子查询和连接。
子查询(EXISTS)写法

SELECT T_Student.Name
FROM T_Student 
WHERE EXISTS (SELECT 1 FROM T_StudentCourse  WHERE T_StudentCourse.StudentId = T_Student.Id AND T_StudentCourse.Score >80
);

连接

select T_Student.Name
from T_Student
inner join T_StudentCourse on T_StudentCourse.StudentId = T_Student.Id
where T_StudentCourse.Score >80

在这里插入图片描述
执行结果里我们发现使用连接语法,结果出现重复。这是因为inner join多表连接的冗余数据,一个学生是存在多门课程大于80分的情况。若用 JOIN 实现,需额外处理去重(避免学生因多条未完成作业重复出现),当只需要关联表的某个字段(而非所有字段)时,子查询可以避免 JOIN 带来的全表字段冗余。

1.3 小结

特性连接查询子查询实现
数据关联方式通过 JOIN 直接合并表通过子查询嵌套获取关联数据
执行顺序先关联表,再过滤结果先执行子查询获取班级 ID,再筛选学生
适用场景多表数据展示场景需分步计算或条件依赖场景
性能 通常更优(数据库可优化连接)可能多次执行子查询(依赖数据库优化)
  • 可读性:原连接查询更简洁直观,子查询版本在复杂场景下可能降低可读性。
  • 等价性:两种写法在功能上完全等价,但执行计划可能不同。

子查询更适合逻辑分层明确、存在性检查、标量值计算等场景,优势在于逻辑清晰、代码简洁;而连接查询(JOIN)更适合需要多表字段关联展示的场景。实际开发中需根据具体需求(如可读性、性能、数据量)选择更合适的方式。

二、常见的子查询

2.1 标量子查询(Scalar Subquery)

标量子查询返回的是单个值,可以作为查询语句中的一个常量使用,通常用于替换主查询中需要单值的位置。
比如我们查找每个学生选修的课程数量。

SELECT T_Student.Name,(SELECT COUNT(*) FROM T_StudentCourse WHERE Id = T_StudentCourse.StudentId) AS CourseCount
FROM T_Student

查询课程数据量的子查询语句返回课程数量这个常量,作为查询结果集中的一个列。

2.2 列子查询(Column Subquery)

列子查询返回一列多行,可以和主查询的结果进行比较或者连接,通常与 IN、NOT IN等运算符配合使用
比如我们查找 计算机科学与技术2024级1班 和 软件工程2024级2班 里的学生。

select T_Student.Name,T_Student.Age
from T_Student
where T_Student.ClassId in (select T_Class.Id from T_Classwhere T_Class.ClassName = '计算机科学与技术2024级1班' or T_Class.ClassName = '软件工程2024级2班'
)

查询两个班级这个列子查询返回的是多行单列值,然后在通过IN查询的一部分,获取出这两个班级的学生。

2.3 表子查询(Table Subquery)

表子查询返回的是一个表或视图,可以嵌套在另一个查询语句中使用
比如我们查找学人工智能导论里成绩大于80的学生。

select * 
from(select * from T_StudentCourse where T_StudentCourse.CourseId =(select Id from T_Course where CourseName = '人工智能导论'))t
wheret.Score > 80;

此处用到了两个子查询,第一个标量子查询返回课程的Id,然后第二个表子查询返回所有匹配的数据,通过一个临时表返回。最后通过成绩大于80的where判断返回结果。

标量子查询,列子查询,表子查询的分类依据是根据放回的结果来判断。标量子查询返回的是一列一列(单个值),列子查询返回一列多行,表子查询返回的是多列多行(一个表或视图)。

三、相关子查询(Correlated Subquery)与非相关子查询(Non-Correlated Subquery)

相关子查询和 非相关子查询 是根据子查询与主查询的依赖关系划分的两类核心子查询类型。二者通过子查询是否依赖主查询来区分。

维度非相关子查询相关子查询
依赖关系子查询不依赖主查询的列(独立执行)子查询依赖主查询的列(逐行关联)
执行次数仅执行一次主查询每一行触发一次子查询
典型场景全局比较局部比较

3.1 非相关子查询

非相关子查询,其子查询独立计算,仅执行一次。
下面我们查询年龄大于全体学生平均年龄的学生姓名、年龄及班级名称

SELECT T_Student.Name, T_Student.Age, T_Class.ClassName 
FROM T_Student
INNER JOIN  T_Class ON T_Student.ClassId = T_Class.Id
WHERE T_Student.Age > (-- 子查询:计算全体学生的平均年龄(独立执行一次)SELECT AVG(Age) FROM T_Student);
  1. 先执行子查询 SELECT AVG(Age) FROM T_Student,得到全体学生的平均年龄(如 18 岁)。
  2. 主查询通过 INNER JOIN 关联学生表和班级表,筛选出年龄大于 18 岁的学生,并返回姓名、年龄和班级名称。

3.2 相关子查询

子查询依赖主查询,主查询每处理一行,子查询重新计算一次
下面我们查询年龄大于所在班级平均年龄的学生姓名、年龄及班级名称。

-- 非相关子查询示例(子查询不依赖主查询的列)
SELECT T_Student.Name, T_Student.Age, T_Class.ClassName 
FROM T_Student
INNER JOIN T_Class ON T_Student.ClassId = T_Class.Id
WHERE T_Student.Age > (-- 子查询:计算当前行班级的平均年龄(依赖主查询的 ClassId)SELECT AVG(Age) FROM T_Student s WHERE s.ClassId = T_Student.ClassId  -- 关键:关联主查询的当前行班级 ID);
  1. 子查询根据当前行的 T_Student.ClassId ,计算该的所有学生的平均年龄。
  2. 比较学生年龄是否该班级平均年龄,若满足则保留该行。
  3. 主查询处理下一行,子查询重新计算下一个班级的平均年龄,通过班级Id重复比较。

总结

以上就是 SQL 中子查询的核心概念,对比了子查询与连接的差异,详述了标量子查询、列子查询、表子查询的类型特点,剖析了相关子查询(依赖主查询逐行执行)与非相关子查询(独立执行一次)的区别。

相关文章:

  • Point-wise vs Pair-wise vs List-wise 简述
  • iperf3 如何测试STA 和wifi direct的 throughput
  • Vue 3 核心知识点全览
  • AI时代新词-多模态(Multimodal)
  • 使用AI生成的产品描述存在的商标侵权风险
  • 一个质谱仪的系统的发布
  • 修改 K8S Service 资源类型 NodePort 的端口范围
  • 鸿蒙OSUniApp 实现动态的 tab 切换效果#三方框架 #Uniapp
  • uniapp-商城-70-shop(3-商品列表,点击规格,进行属性选择)
  • STM32单片机系统HAL库编程实践电子书
  • 车载通信网络 --- OSI模型中物理层和数据链路层
  • AI天气预报进入“大模型时代“:如何用Transformer重构地球大气模拟?
  • 第11章 标准化和软件知识产权基础知识,多媒体、图像相关
  • [PyMySQL]
  • 鸿蒙OSUniApp 开发的图文混排展示组件#三方框架 #Uniapp
  • mysql-tpcc-mysql压测工具使用
  • WIN--文件读写
  • Python函数异常处理底层实现原理
  • OpenLayers 加载ArcGIS瓦片数据
  • Spring AI系列之Spring AI 集成 ChromaDB 向量数据库
  • 网站注册系统用什么做/如何提高网站排名
  • 新余网站制作/十大免费软文推广平台
  • 自适应网站怎么做m站/无锡网站seo顾问
  • 女网友叫我一起做优惠券网站/郑州网站推广效果
  • 江苏省建设信息网/济南seo小黑seo
  • 网站搭建公司排行榜/站内推广有哪些方式