尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
数据库嵌套查询实战:从IN到EXISTS的避坑与优化指南
简介数据库实验5嵌套查询.doc是一份数据库课程实验报告面向正在学习SQL查询的初学者重点讲解统计查询和嵌套查询的语法与实操。压缩包仅含1个doc文档大小约642KB内容覆盖实验目的、统计查询、连接查询、嵌套查询、知识点总结与实验思考章节编排适合按步骤对照操作。文档完整收录了CPXS数据库上的13类典型查询实例既有统计客户数目、求库存量总和、查询最高单价等基础练习也包含查询上海客户订购数量大于200套的订单明细、查找与美美公司在同一城市的客户、检索单价比A01产品更高记录的嵌套查询可帮助读者理解子查询、连接查询和谓词的实际运用场景。读者可通过这些实例加深对SELECT语句、GROUP BY分组、HAVING过滤和子查询等关键语法的理解。该报告在CSDN已有930人学习浏览适合数据库实验作业参考、期末复习或SQL入门自学是一份可直接对照练习的实用资料。1. 嵌套查询实验你以为随手写个 IN 就算会了做过几次数据库实验的人多半觉得嵌套查询是“最没技术含量”的一关不就是子查询套在外面查询里嘛WHERE 后面跟个 IN、跟个 EXISTS 就完事了。可真让你去跑实验5你会撞上很多说不清的现象——明明子查询单独执行没问题合到一起结果就空了用了 EXISTS 性能上去了但条件一换结果就错位更常见的是查询语句写得冗长教授一眼就看穿你不懂执行逻辑。这个实验真正要练的不是“写出一条能出结果”的 SQL而是训练你把每条嵌套查询翻译成“数据库底层到底先跑谁、后跑谁、每行跑几次”。这篇文章从原理讲到可复现的建库、样例数据和五类高频写法再把执行顺序、空值、关联条件这些最容易翻车的点逐个拆开。适合正在做数据库实验的学生也适合想给自己补一轮子查询基础的开发者和准备面试的从业者。2. 嵌套查询的前置认知子查询的类型与执行逻辑2.1 嵌套查询是什么三条边界线先划清楚嵌套查询也叫子查询或内层查询指的是在一个 SELECT、INSERT、UPDATE 或 DELETE 语句内部再嵌套一个完整的查询语句。外层的查询叫父查询内层的叫子查询。动手写之前需要把三个常见混淆点划清楚。第一个混淆点是嵌套查询和连接查询的关系。很多同学以为能用 JOIN 解决的问题就不需要子查询反过来也成立。实际上两者在很多场景下可以互换但执行路径完全不同连接查询在绝大多数数据库里会先做笛卡尔积再过滤优化器会有优化子查询则常常先执行内层、把结果集物化或作为流式条件再驱动外层。选择哪个不只看结果对不对还要看数据量和索引情况。第二个混淆点是相关子查询和非相关子查询。非相关子查询的内层查询独立于外层先执行一次得到一个固定结果集外层拿这个结果集做条件判断。相关子查询的内层引用外层表的列外层每处理一行内层可能都要重新执行一次。这个区别不搞清楚后面查性能瓶颈和排查结果出错都会无从下手。第三个混淆点是子查询出现的位置。多数人只见过 WHERE 后面的子查询但子查询可以出现在 SELECT 列表标量子查询、FROM 后面派生表、HAVING 子句、甚至 JOIN 的 ON 条件里。实验5 如果只写 WHERE 子查询覆盖度是不够的至少要练到 WHERE、FROM、SELECT 三个位置才能应对不同题型的组合。2.2 执行顺序与物化机制为什么单独跑子查询没问题合起来就翻车理解嵌套查询的关键在于搞清楚数据库执行引擎是怎么处理内层和外层的。以某数据库系统为例非相关子查询通常先被优化器识别确定它可以独立物化——也就是把内层查询的结果先算出并缓存或流式输出然后外层再基于这份结果执行过滤。相关子查询则不一样内层条件和外层行绑定引擎会为外层每一行或者每一个分组重新计算内层条件。这里有一个常见翻车点单独执行子查询时返回 300 行结果看着没问题嵌套进外层后变成 0 行。原因多半是子查询结果里包含 NULL。IN 的语义遇到 NULL 时如果左边的值和右边的结果集匹配不上并且结果集里有 NULL整个条件的真值会变成 UNKNOWNWHERE 只保留 TRUE 的行于是结果被过滤光。这种问题在执行顺序层面看不出来必须从三值逻辑层面去排查。接下来需要考虑的情况是物化结果的排序是否影响外层。子查询的结果作为集合使用时不关心顺序但作为派生表FROM 子句时如果外层要分页或取 Top N内层不排序会导致结果不稳定。这也是实验里常被忽略的规范在子查询内部 fin 排序然后外层再包一层查询做分页不能指望数据库自动保留子查询的排列顺序。理解了这两点就可以进入实际操作环节。下面一章把整个实验的数据库环境、建表语句和样例数据铺好保证后面的查询示例可以原样在你的机器上跑通。3. 把实验环境搭起来建库建表与样例数据的准备3.1 用一段 SQL 脚本初始化实验库三个表的设计思路实验5 的嵌套查询需要一个足够“别扭”的库结构——表不能太少字段之间的关联不能太直白否则嵌套查询完全没有发挥空间。常见做法是准备三张表学生表、课程表、选课表。学生表里有学号、姓名、专业、出生年份课程表里有课程号、课程名、学分、授课教师选课表记录学号、课程号、成绩。这三张表构成经典的选课模型既能做 IN 和 EXISTS 的对比也能方便验证 ANY/ALL 和标量子查询。下面给出可直接执行的建表脚本。注意用到的数据库环境是 MySQL 8.x字符集和排序规则统一设置为 utf8mb4 和 utf8mb4_unicode_ci避免中文字段比较出问题CREATE DATABASE IF NOT EXISTS lab5 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE lab5; CREATE TABLE student ( sid CHAR(10) PRIMARY KEY, sname VARCHAR(20) NOT NULL, major VARCHAR(30), birth_year INT ); CREATE TABLE course ( cid CHAR(6) PRIMARY KEY, cname VARCHAR(40) NOT NULL, credit DECIMAL(3,1), teacher VARCHAR(20) ); CREATE TABLE sc ( sid CHAR(10), cid CHAR(6), score DECIMAL(5,1), PRIMARY KEY (sid, cid), FOREIGN KEY (sid) REFERENCES student(sid), FOREIGN KEY (cid) REFERENCES course(cid) );这段脚本里几个设计决策值得说明。学号用 CHAR(10) 而不是 INT是因为学号通常有前导零用整型会丢格式成绩用 DECIMAL(5,1) 而不是 INT是为了能存 59.5 这种半分制成绩也符合多数高校的计分规则。外键在这里不只是形式后面做 EXISTS 相关子查询时外键能保证测试数据的一致性。对于装了旧版本数据库或者不想开外键的同学可以在建表时不写 FOREIGN KEY 部分只保留 PRIMARY KEY这样导入数据时更自由。但注意如果外键约束缺失脏数据会导致某些嵌套查询结果看起来“不合理”排查时优先检查这一层。3.2 灌入样例数据数据量要小但要把坑埋够样例数据的核心原则是“小但全”行数控制在十几条但要包含选课缺勤没有成绩的记录、同一门课多人选修、某人选了多门课、某个学生一门课都没选、某门课没人选这些边界情况。如果数据全是你好我好大家好的标准记录很多嵌套查询的边界条件根本测不出来。INSERT INTO student (sid, sname, major, birth_year) VALUES (20230001, 张一, 计算机, 2005), (20230002, 李二, 计算机, 2004), (20230003, 王三, 软件工程, 2005), (20230004, 赵四, 数据科学, 2006), (20230005, 钱五, 计算机, 2004), (20230006, 孙六, 网络工程, 2005), (20230007, 周七, 软件工程, 2005), (20230008, 吴八, 数据科学, 2006); INSERT INTO course (cid, cname, credit, teacher) VALUES (C001, 数据库原理, 3.0, 刘老师), (C002, 操作系统, 4.0, 陈老师), (C003, 数据结构, 3.5, 王老师), (C004, 计算机网络, 3.0, 李老师), (C005, 软件工程, 2.5, 赵老师); INSERT INTO sc (sid, cid, score) VALUES (20230001, C001, 88.0), (20230001, C002, 76.5), (20230002, C001, 91.0), (20230003, C001, 66.0), (20230003, C003, 73.5), (20230004, C002, 59.0), (20230004, C004, 82.0), (20230005, C001, 45.0), (20230005, C004, 92.5), (20230006, C003, 88.0), (20230007, C002, 70.0), (20230007, C005, 60.0), (20230008, C005, NULL);数据里埋了三个明显的坑位学生钱五有成绩低于 60 的记录学生吴八在选课表里有记录但成绩是 NULL学生孙六只选了一门课周七没选数据库原理。后面写“查选修了全部课程的学生”“查平均分大于某值的学生”这类题时这些边界记录就是验证逻辑的关键。往 MySQL 里执行这份数据前建议把 SQL 文件放在本地用 source 命令整体导入避免在图形化客户端一行行复制漏掉分号。导入完成后先用 SELECT COUNT(*) 分别查三张表确认是 8 名学生、5 门课、13 条选课记录再开始写查询。4. 五种高频嵌套查询的写法与参数说明4.1 IN 子查询最直观但对 NULL 最敏感的写法IN 子查询是实验最开始练习的内容。目标题型通常是“查询选修了课程号为 C001 的学生姓名”或者“查询与张一在同一专业的学生”。用 IN 写内层返回一个列的值集合外层判断每一行的值是否在这个集合里。-- 查询选修了数据库原理C001的学生学号和姓名 SELECT sid, sname FROM student WHERE sid IN ( SELECT sid FROM sc WHERE cid C001 );这条查询的执行路径是先执行内层从选课表里筛出 cid 等于 C001 的学号集合然后外层遍历学生表逐行判断学号是否在这个集合中。如果 sc 表里对应的 cid 不存在内层返回空集IN 的判断结果全部为 FALSE外层无输出——这是想要的行为。但要注意一个变体。如果把内层改成查询“没有选修任何课程的学生”一些初学者会写成WHERE sid NOT IN (SELECT sid FROM sc)这看起来合理但如果 sc.sid 列里存在 NULLNOT IN 的结果是整个查询变成 0 行而不是返回没选课的人。这条在实验里就是经典的反直觉坑。想验证的话可以在 sc 表里插入一条 sid 为 NULL 的记录再跑这条查询结果会让所有记录消失。需要记住的参数语义是IN 等价于 ANYNOT IN 等价于 ALL。但 NOT IN 遇到 NULL 时的行为和 ANY/ALL 不同具体差异在避坑章节单独展开。4.2 EXISTS 相关子查询外层驱动内层场景覆盖更广EXISTS 是嵌套查询里的重点题型几乎每个实验都会有一道“查询选修了所有课程的学生”或“查询没有选修任何课程的学生”。EXISTS 的核心是内层查询不返回具体列值只返回真值——只要内层查询结果集非空EXISTS 条件即成立内层为空则不成立。相关子查询的形式是内层引用外层表的列。-- 查询没有选修任何课程的学生 SELECT sid, sname FROM student s WHERE NOT EXISTS ( SELECT 1 FROM sc WHERE sc.sid s.sid );这里内层的sc.sid s.sid是关联条件把外层 student 表的每一行带入内层判断。执行过程通俗讲数据库先取学生表的第1行拿它的学号去选课表里查找有无匹配记录有则 NOT EXISTS 为假过滤掉没有则保留。接着处理第2行重复这个过程。很多教材会强调 SELECT 后面写 1 而不是*原因是 EXISTS 只关心结果集是否非空不关心具体列写 1 在语义上更准确执行计划里也更有可能走半连接优化。从实验评分角度看写成SELECT *也不会错但导师如果看代码规范通常会提示改成 1 或某一列常量。EXISTS 适合的场景是判断“存在性”和“全称量词转换”。比如查选修了全部课程的学生经典做法是双重 NOT EXISTS找一个学生不存在某门课是他没选的。这个写法在 4.4 专门展开。初学者容易把 EXISTS 和内层返回多列混在一起实际上 EXISTS 根本不关心里面选了几列只要非空就行。4.3 标量子查询与 FROM 子查询两个容易被忽视的位置嵌套查询不只出现在 WHERE 里。标量子查询出现在 SELECT 列表里返回一个单一值就像查询结果里多了一列计算字段。FROM 子查询则是把内层查询的结果当作一张临时表外层再基于这张表做聚合或过滤。-- 查询每门课的课程名及其最高分用 FROM 子查询 SELECT c.cname, t.max_score FROM course c JOIN ( SELECT cid, MAX(score) AS max_score FROM sc WHERE score IS NOT NULL GROUP BY cid ) t ON c.cid t.cid;这条语句的执行顺序是先执行括号内的分组聚合得到每个课程号的最高分临时表 t再把 course 表和 t 做连接取出课程名称。这里有一个细节内层里写了WHERE score IS NOT NULL目的是避免把 NULL 成绩当作有效数据参与 MAX 计算。虽然 MAX 函数本身会忽略 NULL但我们在 GROUP BY 分组前先过滤可以让结果的语义更加明确——尤其在需要统计“有效选课人数”时这个过滤是必须的。标量子查询的典型写法是这样的-- 查询学生姓名和选课门数标量子查询 SELECT sname, (SELECT COUNT(*) FROM sc WHERE sc.sid student.sid ) AS course_count FROM student;注意这里内层引用了外层的 student.sid是一个相关的标量子查询。外层有多少行内层就执行多少次。如果学生表有 8 行内层 COUNT 会被执行 8 次。数据量小的时候没问题量大了性能会很差这是它在生产环境里不受欢迎的原因。实验里只要数据量控制住用它来理解相关子查询的执行频率非常直观。4.4 全称量词的转化用双重 NOT EXISTS 查“选修了全部课程”很多实验题里有一道压轴题“查询选修了全部课程的学生姓名。”直接用自然语言翻译成 SQL 是困难的因为 SQL 没有直接的“全称量词”语法。常见解法是把这个命题取逆否找一个学生如果不存在任何一门课程是他没选的那他就选修了全部课程。翻译成 SQL 就是双重 NOT EXISTS。SELECT sid, sname FROM student s WHERE NOT EXISTS ( SELECT 1 FROM course c WHERE NOT EXISTS ( SELECT 1 FROM sc WHERE sc.sid s.sid AND sc.cid c.cid ) );这个查询分三层理解。最内层判断“这个学生是否选了这门课”通过学号和课程号两个条件同时匹配中间层的逻辑是“是否存在一门课该学生没选”也就是最内层查询为空最外层的 NOT EXISTS 再把结果取反变成“不存在任何一个没选的课”等价于“全选了”。用实验里的样例数据跑结果应该是学生张一、赵四。张一选了 C001、C002赵四选了 C002、C004但他们各自并没有选满全部 5 门课。等一下——这里有个重要的语义陷阱课程表里一共 5 门课没有学生选了全部 5 门所以查询结果应该为空集但如果写成查询“选了课程表中的全部课程”而不是“选了 sc 表中出现过全部课程”就能看出两套结果。两种语义的差别在于课程表里的课程是所有可选课选课表里的课程是有学生选过的课。如果某个课程无人选它依然在 course 表里学生是不可能“选修全部课程”的。因此双重 NOT EXISTS 是正确语义。如果某同学写成COUNT(DISTINCT sc.cid) (SELECT COUNT(*) FROM course)思路也对但在空值处理和分组逻辑上更容易出错这里优先推荐 NOT EXISTS 写法。4.5 ANY 与 ALL 子查询比较符配上极值查询ANY 和 ALL 在实验中的典型应用是“查询比某一专业任意学生年龄都大的学生”或“查询比所有计算机专业学生成绩都高的选课记录”。它们的语法位置和 IN 相似但比较符不同。-- 查询比计算机专业任意一个学生出生年份早的学生即年龄更大 SELECT sid, sname, birth_year FROM student WHERE birth_year ANY ( SELECT birth_year FROM student WHERE major 计算机 );内层先查出所有计算机专业学生的出生年份集合例如 {2005, 2005, 2004, 2004}。然后外层逐行比较只要某个学生的出生年份小于集合里的任何一个值条件成立。这里要注意比较方向出生年份越小年龄越大。 ANY的意思是“小于其中任意一个”等价于 “小于集合中的最大值”。反过来 ALL等价于“大于集合中的最大值”。一个常见做法是把 ANY/ALL 改写成聚合函数形式 ANY改成 MAX ALL改成 MAX。实验结果可以验证两个写法返回一样的结果但聚合写法往往执行效率更高因为优化器更容易利用索引。实验报告的题目如果要求两种写法都给出这种等价改写就是很好的加分点。在使用 ANY 和 ALL 时必须小心子查询结果包含 NULL。如果内层结果集里存在 NULLANY 的结果可能为 TRUE只要有一个真值就行而 ALL 只要遇到 NULL 就会把整个比较变为 UNKNOWN。比如score ALL (SELECT score FROM sc WHERE ...)如果该子查询结果里包含 NULL那所有外层行的条件都是未知最后查询结果可能就是 0 行。实验里这个问题非常隐蔽建议在写 ALL 时内层加WHERE score IS NOT NULL提前规避。5. 嵌套查询实验的避坑与排查指南5.1 坑一NOT IN 遇上 NULL全表结果消失现象写SELECT ... WHERE sid NOT IN (SELECT sid FROM sc)想找没选课的学生结果一条都不返回。原因这是三值逻辑问题。当子查询结果 sid 集合中包含 NULL 时NOT IN 的语义要求外层行的 sid 不等于集合里的每一个值。但只要和 NULL 比较结果就是 UNKNOWNWHERE 只保留 TRUE。也就是说集合里只要有一个 NULL所有行的 NOT IN 判断都可能变成 UNKNOWN于是整条查询结果为空。解决两个方向。一是给 sc 表的 sid 加 NOT NULL 约束从源头杜绝二是改写为 NOT EXISTS因为 EXISTS 对 NULL 免疫SELECT sid, sname FROM student s WHERE NOT EXISTS ( SELECT 1 FROM sc WHERE sc.sid s.sid );这条在 4.2 已经验证过结果会正确返回吴八成绩为 NULL 但存在选课记录之外没有选课记录的学生。需要反思的不仅是写法而是真正理解 SQL 的三值逻辑任何与 NULL 的比较产物都不是 TRUE 或 FALSE而是 UNKNOWN。以后凡是写带 NOT 的子查询第一反应先检查内层相关列有没有 NULL 风险。5.2 坑二关联条件漏写内层查询被当成独立查询执行现象写相关子查询时内层 WHERE 里只写了内层表自己的条件漏了外层表.列名 内层表.列名的关联。结果查询没有报错但返回行数比预期多出很多或者结果完全不符合题目。原因漏写关联条件后EXISTS 会怎样看这段有问题的写法SELECT sname FROM student s WHERE EXISTS ( SELECT 1 FROM sc WHERE cid C001 );内层查询没有引用外层 s 的任何列于是 EXISTS 只是一个固定的真值判断——只要 sc 表里存在 C001 的记录整个 EXISTS 恒为真。结果是返回所有学生姓名而题目可能要求“选修了 C001 的学生”。这个结果看起来“像是查出来了”但多了一堆没选课的人。解决写相关子查询的固定习惯是内层 WHERE 里先写关联条件再写自己的过滤条件。可以按下面顺序组织减少漏写概率SELECT sname FROM student s WHERE EXISTS ( SELECT 1 FROM sc WHERE sc.sid s.sid AND sc.cid C001 );这在做实验时怎么排查拿一条错误结果出来先随机挑一行返回记录用它的学号手动去 sc 表查询有没有对应选课记录。如果没有基本就是关联条件漏了。另一个办法是把 EXISTS 改成连接查询看结果是否一致。5.3 坑三内层子查询的排序和分组被外层“吃掉”现象FROM 子查询里写了 ORDER BY然后外层再做过滤或分页得到的结果顺序不稳定甚至排序完全失效。比如说“查每门课最高分的学生学号”内层排了序外层一包顺序就乱了。原因数据库不保证子查询的排序结果在派生表里得到保留因为没有语义要求。除非外层也有 ORDER BY否则结果集的顺序由优化器决定。这是 SQL 标准的行为不是数据库 bug。解决不要在内层 ORDER BY 指望它影响最终结果顺序外层必须重新 ORDER BY。如果要做“每组取最大”这类需求建议用窗口函数替代子查询排序例如SELECT cid, sid, score FROM ( SELECT sc.*, ROW_NUMBER() OVER (PARTITION BY cid ORDER BY score DESC) AS rn FROM sc ) t WHERE t.rn 1;这条里 PARTITION BY cid 是分组窗口ORDER BY score DESC 在分组内排名外层再过滤 rn 1 取每组最高。它避免了子查询排序和派生表的顺序不确定问题语义也更好。5.4 坑四EXISTS 写对了但性能极差没有走半连接现象数据量只有几百行时EXISTS 查询秒出。但把数据量放大到几万行后相关子查询变成逐行扫描耗时从几十毫秒涨到几秒甚至更久。原因相关子查询的执行方式天然是“外层行数 × 内层查找开销”。如果没有合适的索引内层每次关联都在做全表扫描。外层 8 行时无所谓外层 8 万行时就会爆炸。解决给关联列和外层过滤列建索引。以本实验的模型为例CREATE INDEX idx_sc_sid ON sc(sid); CREATE INDEX idx_sc_cid ON sc(cid); CREATE INDEX idx_sc_sid_cid ON sc(sid, cid);建完索引后用 EXPLAIN 看执行计划EXISTS 相关子查询正常情况下会显示为半连接semi join。如果看不到检查数据库优化器是否被参数限制或者查询写成了外面套一层视图的复杂形式。实验数据量不大时性能差异不明显但养成看执行计划的习惯后面做数据分析或生产环境排查时会省很多事。5.5 坑五成绩表里是 NULL 却被当成 0 分参与比较现象“查询成绩低于平均分的学生”这类题错误结果经常少一条或甚至多一条。因为 sc 表里那条成绩为 NULL 的选课记录在某些聚合计算里被忽略在另外一些比较里又行为诡异。原因AVG、MAX、MIN 等聚合函数会忽略 NULLCOUNT(*) 不会忽略。如果先用AVG(score)求出平均分再用score 平均分做比较NULL 成绩的行不会进入结果集因为 NULL 与任何数比较都是 UNKNOWN。如果反过来写NOT score 平均分结果仍然不含 NULL 行。所以缺失成绩的学生既不能算“低于平均”也不算“高于平均”而是独立的存在。解决明确需求后补过滤。如果要统计“成绩有效”的选课记录先写WHERE score IS NOT NULL如果要统计“所有选课记录”的平均分聚合函数内部自然忽略 NULL 即可。警惕点在于两条查询结果不同时要能解释差异来源。建议在实验报告里专门写一段“空值说明”说明 NULL 成绩的行如何被排除、为什么排除。6. 嵌套查询的验证与优化进阶从跑通到跑好嵌套查询写完能出结果只是实验的及格线。往上走一步需要掌握两类能力验证结果正确性和改写优化。验证正确性的一个习惯性做法是用“集合语义”来检查——把嵌套查询改写为连接查询看两个结果集是否完全一致。拿 4.1 的 IN 查询举例等价写法是SELECT DISTINCT s.sid, s.sname FROM student s JOIN sc ON s.sid sc.sid WHERE sc.cid C001;IN 会自动去重而 JOIN 不会所以这里必须写 DISTINCT 才能保证结果一致。这个对比过程本身就是理解嵌套查询和连接查询差异的最好训练。常见改写方案还有IN 改 EXISTS、相关子查询改窗口函数、双重 NOT EXISTS 改分组计数比对。分组计数比对的写法是SELECT sid FROM sc GROUP BY sid HAVING COUNT(DISTINCT cid) (SELECT COUNT(*) FROM course);这组写法和双重 NOT EXISTS 在这个实验的数据里会得到同样结果但语义略不同前者要求“该学生选修的课程数等于课程总数”后者要求“不存在的课程数为 0”。如果课程表里有无人选修的课两种写法结果仍会一致但如果选课表里存在同一学生重复选同一门课理论上外键约束禁止写法就会分化。写实验报告时可以并行给出并解释细微差别这是拿高分的关键。索引的进阶用法也要提给 sc 表建好联合索引 (sid, cid) 后EXISTS 内层的关联查找会走索引外层驱动方式从逐个探测变成批量探测。用 EXPLAIN 观察 type 字段从 ALL 变成 ref 或 eq_ref这就是物理层面优化生效的证据。可以做一个数据量放大实验把 sc 表复制到 10 万行相同的选课模式重复多次对比建索引前后查询耗时记录到实验报告里作为优化论证。另一个进阶技巧是理解半连接和物化的取舍。某些场景下优化器会把 NOT IN 改写为 anti join效果和 NOT EXISTS 一致但优化器不是万能的复杂嵌套层级多了之后自动改写可能失败。此时手动改写是可控的兜底方案。我的个人习惯是子查询不超过两层时用可读性好的写法超过两层时先跑通再用 EXPLAIN 分析最后根据执行计划决定是否改写。不迷信“EXISTS 一定比 IN 快”在数据量小、有合适索引的情况下给优化器空间让它自己选。回到实验本身给正在做实验5的同学一句经验不要同时打开多个结果窗口反复比对。把每次查询的预期结果先写注释再执行 SQL结果不一致时优先检查关联条件和 NULL 行为——我当年在这个实验上花时间最多的地方不是写 SQL而是理解为什么单独执行结果和嵌套执行结果会不一样。后来养成的习惯是任何嵌套查询写完后里层子查询单独拉出来执行一次看结果集形状是否符合预期再看外层。坚持这个习惯之后嵌套查询的错误率至少降了一半。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

MATLAB数据分析与挖掘实战:完整源码、数据清洗与模型评估指南

MATLAB数据分析与挖掘实战:完整源码、数据清洗与模型评估指南

简介:面向工科生、数学专业与算法方向学习者的MATLAB数据分析实战教程,以完整案例驱动方式覆盖数据预处理、特征分析、可视化及常用挖掘模型,适合需要快速上手并提升工程项目仿真能力的读者。资源共853个文件,包括653个m源码脚本、…

📅 2026/10/9 13:50:18
问题记录——SQLite 报错 Couldn‘t read row 0, col -1 from CursorWindow:从 Cursor 越界到列索引排查的完整复盘

问题记录——SQLite 报错 Couldn‘t read row 0, col -1 from CursorWindow:从 Cursor 越界到列索引排查的完整复盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/9 13:50:18
mypy-boto3-efs Python 包实战指南:为 Boto3 EFS 客户端接入完整静态类型检查

mypy-boto3-efs Python 包实战指南:为 Boto3 EFS 客户端接入完整静态类型检查

【免费下载链接】context-hub 项目地址: https://gitcode.com/gh_mirrors/co/context-hub 点击查看 免费下载 mypy-boto3-efs 是专为 AWS EFS(Elastic File System)服务生成的 boto3 类型注解包,用于在 Python 项目中获得 mypy /…

📅 2026/10/9 13:50:18
MORE NEWS

更多资讯

📰

CCNP PDF课程资料学习指南:从理论基础到实验验证的网络工程师进阶路线

简介:思科CCNP课程.pdf是一份根据培训机构内部PPT整理而成的CCNP学习笔记,作者边看边记,适合备考CCNP或负责企业级网络设计、实施与排障的网络工程师。内容覆盖TCP/IP协议回顾、VLAN/Trunk/VTP部署、生成树STP与RSTP、二层与三层交换、链路聚…

📰

基于C#和MySQL的房屋租赁管理系统课程设计完整资源

简介:基于C#与MySQL的房屋租赁管理系统完整项目包,面向计算机、软件工程、通信工程等专业学生,适合作为课程设计或毕业设计参考,也适合有一定C#基础的学习者通过完整项目理解业务系统开发流程。压缩包共69个文件,约12.…

📰

CNC模具加工全流程实战:从开粗到精加工的工艺路线与参数详解

1. 从一张报废的模仁说起:CNC模具加工到底难在哪干了十几年CNC,我见过太多人把模具加工想简单了。很多人觉得,不就是把一块钢料按图纸铣出来吗?三轴机床跑个刀路,尺寸到位就完事了。但真正在模具厂待过的人都知道&…

📰

Java物联网通用驱动包:统一Modbus、Bacnet、OPC-UA协议对接

简介:这是一套基于Java开发的物联网IOT通用驱动包源码,面向需要快速集成多种工业通信协议的Java开发者、系统集成商及物联网项目团队,帮助解决Modbus-TCP、Bacnet、OPC-UA等协议接入繁琐、重复造轮子的问题。资源包共76个文件,约1…

📰

RTThread HardFault定位实战:寄存器分析与栈回溯方法

1. 从一次深夜调试说起:为什么HardFault定位值得单独拿出来讲搞嵌入式的人大概都有过这种经历:板子跑着跑着突然就不动了,串口没有任何输出,调试器一连上发现程序停在了一个叫HardFault_Handler的死循环里。这时候你盯着屏幕&…

📰

Java Web文件夹递归上传与SM4加密落盘:从JSP到Servlet完整实现

接到过几个类似的需求,都是内网里的文件管理系统,要求挺一致:Java后台、JSP做页面、用户能直接在网页上选整个文件夹,把里面多层级的目录结构和文件一次性传上来,落盘后文件还不能是明文的。这个“文件夹递归上传 服务…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬