尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

SQL经典练习题解析:如何查询同时使用红色螺母和蓝色螺丝刀的工程

发布时间:2026/9/17 22:14:55

资讯中心
01
ARTICLE

SQL经典练习题解析:如何查询同时使用红色螺母和蓝色螺丝刀的工程

SQL经典练习题解析:如何查询同时使用红色螺母和蓝色螺丝刀的工程
在数据库圈子里混久了就会发现教科书上的题往往比很多生产环境的SQL更能考验我们对关系运算的理解。前几天整理SQL题库时我重新过了一遍供应商-零件-工程项目S-P-J这套经典关系数据库的练习题其中一道题让我印象格外深就是10-94这道查询同时使用红色的螺母零件和蓝色的螺丝刀零件的工程。题面简洁到只有一句话但它把集合语义、多表关联、子查询和去重时机全都串起来了几乎可以当成SQL进阶的综合性考题。这篇文章就围绕这道题展开从表结构设计到几种SQL写法再到实际跑数据时的调优和踩坑一次性聊透。正在准备面试的朋友可以当复习材料平时写业务查询的开发同学也能从“同时使用”这种看似简单却容易写错的条件里找到一些通用的排查思路。1. 从10-94这道题看教学数据库里的经典场景1.1 SPJ到底在模拟什么业务凡是系统学过关系数据库的人基本都绕不开S-P-J这组表。供应商S、零件P、工程项目J以及中间的供应关系SPJ其实是在模拟一条非常典型的供应链业务若干个供应商向不同的工程项目供货每个项目需要多种零件每种零件又可以由多个供应商提供。日常生产里的采购系统、物料系统、库存系统本质上跟它是一样的只不过表名换成了t_supplier、t_material、t_project之类。这道题里特意把零件限定为“红色的螺母”和“蓝色的螺丝刀”说明在同一套数据里零件表P中同名或同颜色的零件可能不止一种比如红色螺母可能有好几个规格蓝色螺丝刀也可能有不同型号。而在SPJ关系表中一个工程的编号JNO会对应多条供应记录每条记录关联一种零件。要判断一个工程是否“同时使用”了两类零件不能简单地在一条记录里找两个条件同时成立因为一条SPJ记录只能对应一种零件红色螺母和蓝色螺丝刀必然出现在不同的记录行里。这也是教学题最有价值的地方它逼着你从“行内条件叠加”的惯性思维里跳出来切换到“集合之间的包含关系”这个维度。你找的不是某一行满足A且B而是这个工程的零件集合同时包含A类和B类。1.2 题面里三个隐藏的查询要求很多人一上来就写WHERE P.COLOR红 AND P.PNAME螺母 AND P.COLOR蓝 AND P.PNAME螺丝刀这种写法一眼看过去就自相矛盾因为一条记录不可能同时是红色又是蓝色。就算有人把两组条件拆开用OR连接那也只能得到“至少使用其中一种零件”的工程跟题目的“同时使用”仍然不是一回事。拆解一下“同时使用红色的螺母零件和蓝色的螺丝刀零件”这个句子里面其实包含三个逻辑要求第一这个工程里必须存在至少一条供应记录关联的零件是红色螺母第二这个工程里必须存在至少一条供应记录关联的零件是蓝色螺丝刀第三上面两个条件针对的是同一个工程编号说的是同一个JNO。换句话说题目的本质是求两个“工程编号集合”的交集。第一个集合是“使用过红色螺母的工程编号集合”第二个集合是“使用过蓝色螺丝刀的工程编号集合”取交集后再回到J表里把工程名称等信息查出来。只要把这个语义理清了写SQL的思路就会非常清晰后面各种写法都可以看作是对这个语义的不同实现方式。2. 建表与测试数据准备2.1 经典四表结构与约束要跑通这道题先得有一份可以反复折腾的实验环境。经典的S-P-J数据库包含四张表在实际教学和练习中通常这样建CREATE TABLE S ( SNO CHAR(2) PRIMARY KEY, SNAME VARCHAR(20), STATUS INT, CITY VARCHAR(20) ); CREATE TABLE P ( PNO CHAR(2) PRIMARY KEY, PNAME VARCHAR(20), COLOR VARCHAR(10), WEIGHT INT ); CREATE TABLE J ( JNO CHAR(2) PRIMARY KEY, JNAME VARCHAR(20), CITY VARCHAR(20) ); CREATE TABLE SPJ ( SNO CHAR(2), PNO CHAR(2), JNO CHAR(2), QTY INT, PRIMARY KEY (SNO, PNO, JNO) );这里有几个细节值得注意。S表里的供应商编号是主键P表里的零件编号是主键J表里的工程编号是主键而SPJ表则是用三个外键组合成联合主键。之所以联合主键能成立是因为业务上允许同一个供应商给同一个工程供应同一种零件时数据合并成一行用QTY字段记录供应总量如果允许同一对组合出现多条记录那就还需要再引入流水号字段。我个人在实际建表练习时通常会顺手把外键约束也加上虽然做查询题的时候外键不影响结果但加外键能更真实地模拟生产环境而且在做删除和更新操作时能避免把数据搞乱。需要注意的是在学校教材里SPJ表的字段名有时也写成PNUM、JNUM或者QTY之外的别名做题目之前先看清题目给的表名和字段名。2.2 造一套能复现题目的模拟数据练习查询最重要的是数据能不能覆盖各种边界情况。我只造了几条关键数据来演算这道题但如果你自己练建议多造几组。INSERT INTO P VALUES (P1, 螺母, 红, 12); INSERT INTO P VALUES (P2, 螺丝刀, 蓝, 20); INSERT INTO P VALUES (P3, 螺母, 蓝, 14); INSERT INTO P VALUES (P4, 螺丝刀, 红, 25); INSERT INTO P VALUES (P5, 螺栓, 红, 18); INSERT INTO J VALUES (J1, 造船工程, 上海); INSERT INTO J VALUES (J2, 桥梁工程, 武汉); INSERT INTO J VALUES (J3, 汽车生产线, 广州); INSERT INTO J VALUES (J4, 发电站建设, 成都); INSERT INTO SPJ VALUES (S1, P1, J1, 200); INSERT INTO SPJ VALUES (S2, P2, J1, 150); INSERT INTO SPJ VALUES (S3, P3, J2, 90); INSERT INTO SPJ VALUES (S1, P4, J2, 60); INSERT INTO SPJ VALUES (S2, P1, J3, 120); INSERT INTO SPJ VALUES (S2, P5, J1, 80); INSERT INTO SPJ VALUES (S1, P2, J4, 45); INSERT INTO SPJ VALUES (S3, P4, J3, 70); INSERT INTO SPJ VALUES (S1, P1, J4, 30); INSERT INTO SPJ VALUES (S2, P2, J4, 55);这套数据里特意设计了几种情况J1同时用到了红色螺母P1和蓝色螺丝刀P2是正确答案J2用了蓝色螺母P3和红色螺丝刀P4虽然颜色和工具类型都出现了但颜色和名称的搭配对不上不属于题目要求J3只用了红色螺母缺少蓝色螺丝刀J4虽然同时用到了P1和P2但如果某道题里只想查“红色螺母P1”和“蓝色螺丝刀P2”那J4也符合。为了让结果更直观我在原本的测试集里把J4也设计成同时具备这两类零件的工程方便验证DISTINCT和JOIN组合是否会产生重复结果。3. 三种可行的SQL写法与思路拆解3.1 直接用JOIN连接零件条件和工程条件最容易被初学者接受的是把SPJ表自连接两次每次连接一种零件条件然后关联到工程表。SELECT DISTINCT J.JNO, J.JNAME FROM J JOIN SPJ AS SPJ_RED ON J.JNO SPJ_RED.JNO JOIN P AS P_RED ON SPJ_RED.PNO P_RED.PNO AND P_RED.PNAME 螺母 AND P_RED.COLOR 红 JOIN SPJ AS SPJ_BLUE ON J.JNO SPJ_BLUE.JNO JOIN P AS P_BLUE ON SPJ_BLUE.PNO P_BLUE.PNO AND P_BLUE.PNAME 螺丝刀 AND P_BLUE.COLOR 蓝;这段SQL的基本逻辑是先把J和SPJ_RED连接筛选出用了红色螺母的工程再把这个结果集和SPJ_BLUE连接筛选出同时用了蓝色螺丝刀的工程。最后加DISTINCT是因为同一工程可能有多个供应商供应红色螺母或者有多条SPJ记录直接JOIN可能产生重复的工程号。从执行过程的角度理解这种写法本质上是做了两次过滤。第一次过滤得到集合A也就是使用红色螺母的工程列表第二次过滤是在A的基础上继续关联蓝色螺丝刀得到的是同时满足两个条件的工程。整个过程非常直观而且如果WHERE条件不小心写进了JOIN子句执行计划也不会报错但结果可能完全不同。需要注意这里我把P表中的颜色和名称条件直接写在JOIN的ON子句里而不是写成WHERE。这两种位置对INNER JOIN来说结果往往是一样的但由于SQL的语义是先连接后过滤ON和WHERE的作用时机不同。在实际业务中如果用的是LEFT JOINON和WHERE的结果差异就会非常大。养成好习惯把连接条件和过滤条件分层写逻辑会清楚很多。3.2 用IN加AND表达集合交集如果不习惯自连接可以用两个IN子查询加AND来实现。SELECT J.JNO, J.JNAME FROM J WHERE J.JNO IN ( SELECT SPJ_RED.JNO FROM SPJ SPJ_RED JOIN P P_RED ON SPJ_RED.PNO P_RED.PNO WHERE P_RED.PNAME 螺母 AND P_RED.COLOR 红 ) AND J.JNO IN ( SELECT SPJ_BLUE.JNO FROM SPJ SPJ_BLUE JOIN P P_BLUE ON SPJ_BLUE.PNO P_BLUE.PNO WHERE P_BLUE.PNAME 螺丝刀 AND P_BLUE.COLOR 蓝 );这种写法在逻辑上最接近“集合求交集”的语义。第一个子查询返回所有用过红色螺母的工程编号第二个子查询返回所有用过蓝色螺丝刀的工程编号外层J表只保留那些同时出现在两个集合里的编号。它的一个好处是不需要考虑DISTINCT问题。因为IN子查询天然只关心“是否存在”即使同一个工程在子查询结果里出现多次也不会影响外层判断。这在数据量大的时候尤其省心不用反复去思考JOIN之后结果集的行数增长情况。另一个好处是写法容易扩展。如果题目再加一个条件比如还要使用绿色的扳手只需要继续在后面加一行AND J.JNO IN (SELECT ...)代码改动的成本极低。当然这种写法也有短板。在市面上常见的关系型数据库里如果子查询的结果集特别庞大而优化器能力又有限可能出现子查询被反复执行的情况。生产环境里遇到这种问题时可以先在子查询里把结果集算小或者考虑改成临时表、CTE这些方案我们在第六节里讨论。3.3 用EXISTS判断存在性在面试中非常受青睐的写法是EXISTS它把“同时使用”翻译成“存在一条记录满足红色螺母并且存在另一条记录满足蓝色螺丝刀”。SELECT J.JNO, J.JNAME FROM J WHERE EXISTS ( SELECT 1 FROM SPJ SPJ_RED JOIN P P_RED ON SPJ_RED.PNO P_RED.PNO WHERE SPJ_RED.JNO J.JNO AND P_RED.PNAME 螺母 AND P_RED.COLOR 红 ) AND EXISTS ( SELECT 1 FROM SPJ SPJ_BLUE JOIN P P_BLUE ON SPJ_BLUE.PNO P_BLUE.PNO WHERE SPJ_BLUE.JNO J.JNO AND P_BLUE.PNAME 螺丝刀 AND P_BLUE.COLOR 蓝 );EXISTS和IN的区别在语义上很微妙IN是拿外层工程编号去子查询结果集里找匹配EXISTS则是针对外层每一行去判断子查询是否非空。对于这道题而言因为子查询里都有J.JNO的相关条件EXISTS写法是一种“相关子查询”每条J记录执行时都会把自身编号带进子查询。很多经验丰富的开发者在处理“是否存在”类型的业务时会更倾向EXISTS。尤其在子查询的关联字段存在索引的情况下EXISTS往往能提前终止扫描只要找到一条满足条件的记录就会返回真不必把整个子查询结果集全部算完。比如判断某个工程有没有使用红色螺母时数据库一旦在SPJ表上通过JNO索引找到第一条匹配P1的记录就可以立刻得出结论不需要继续扫描这个工程的其他供应记录。但要注意EXISTS这种相关子查询的方案最大的风险是外层表数据量很大时理论上可能造成逐行循环。不过在工程J表通常不会太大、而SPJ表会很大的场景下配合索引使用EXISTS往往表现不错。具体性能差异放到执行计划部分一起对比。3.4 三种写法到底怎么选先给一张对照表总结三种写法的特点后面在性能部分会再结合实际执行计划展开。写法核心语义去重处理可读性性能场景适用情况JOIN连接把两个条件当成两条“路径”在关联中完成过滤容易产生重复需要DISTINCT最直观但逻辑容易被JOIN顺序带偏数据量中等索引合理结果集字段需要来自多表时推荐IN子查询两个集合求交集天然去重无需DISTINCT最好理解翻译成自然语言几乎无差别子查询结果可控时稳定条件叠加较多时扩展方便EXISTS相关子查询逐行判断是否存在满足条件的记录天然去重稍难理解但表达“存在”语义最准确外层表小、内层表大且索引好时最优生产环境里判断存在性常用我个人的习惯是写业务代码时优先用EXISTS因为它的“存在即真”语义跟业务描述完全一致写一次性分析SQL时优先用IN子查询因为逻辑清晰不容易出错只有在需要同时返回零件名称、颜色等冗余字段时才会考虑用JOIN。没有绝对的最优写法只有最适合当前场景的写法。4. 关系代数视角这道题背后的“除法”影子4.1 为什么教材总爱出这种题从关系代数来看这道题并不单纯是等值连接它其实已经踩到了“除运算”的门槛。所谓除运算通常表达的是“找出满足所有要求”的记录。比如“查询使用了全部零件的工程编号”这才是标准的关系除法。而“同时使用红色螺母和蓝色螺丝刀”要求的数量是确定的两种本质上可以看作除法的一个特例也可以看作“按指定零件集合做包含关系判断”的简化版。这也是为什么教学数据库喜欢拿它当经典题。它不会像纯除运算那么抽象但又能让学习者接触到“把目标拆成多个集合再通过交集或嵌套来完成判断”的思维方式。理解了这道题再去啃“查询使用了全部零件的工程”“查询至少使用了P1和P2两种零件的工程”之类题目思路会顺很多。4.2 用关系代数表达式拆解执行路径用关系代数来描述一下题目的运算步骤对理解SQL的执行也非常有帮助。设使用红色螺母的工程集合为A使用蓝色螺丝刀的工程集合为B则有A π_JNO(σ_PNAME螺母 AND COLOR红(P) ⋈ SPJ)B π_JNO(σ_PNAME螺丝刀 AND COLOR蓝(P) ⋈ SPJ)最终结果 π_JNO,JNAME(J ⋈ (A ∩ B))这里面最关键的运算就是π_JNO也就是投影。无论中间做了多少次连接最后留在集合里的永远只有工程编号这一列。正因为每一步都给JNO做了投影去重集合A和集合B里的元素才不会重复交集运算的结果才准确。如果没有投影就直接求交集可能会因为重复元组的存在让结果集的含义变成“匹配次数大于0”一旦写SQL时忽略DISTINCT就会出现重复工程号这也是为什么前面反复强调去重时机。在当年没有可视化数据库的年代这本书的练习题就是用这样的关系代数表达式一步步推演的。现在我们有各种SQL工具但回归关系代数去理解仍然能帮我们避开很多“看SQL结果好像对其实是靠DISTINCT兜底”的坑。5. 实际跑通题目结果验证与常见错误排查5.1 验证结果集是否正确按我上一节造的数据执行查询正确的结果应该只有J1和J4。J1的供应记录里有红色螺母P1和蓝色螺丝刀P2J4也有这两类零件两者都符合题目条件。J2虽然有螺母和螺丝刀但颜色和名称的搭配是“蓝色螺母”加“红色螺丝刀”不符合条件。J3只有红色螺母缺少蓝色螺丝刀也不符合条件。如果你用JOIN写法在加上DISTINCT之前查询结果里很可能出现重复的J1和J4。造成这种重复的原因很常见J1工程同时有多条记录比如S1供应了P1S2也供应了P1多条记录都指向同一个JNOJOIN之后结果集自然就会膨胀。这时候DISTINCT就不是一个可选项而是必选项。很多初学者看到JOIN版本结果正确就以为DISTINCT只是锦上添花其实一旦数据量变大漏掉DISTINCT会导致严重的重复统计。用IN或EXISTS版本执行时因为子查询的判断天然去重结果就不会有重复问题。这也是我在给自己写的SQL做自检时的一个习惯同一道题至少用两种写法跑一遍对比结果是否一致。如果出现差异一定是某种写法在条件或去重上出了问题。5.2 最容易犯的几个SQL错误针对这种“同时使用A和B”的题目我总结了几个从初学者到有一定经验的人都很容易踩的坑第一个坑条件放错位置。比如把红色螺母的条件放在JOIN的ON里把蓝色螺丝刀的过滤条件放在WHERE里结果也可能正确但错误风险更高。尤其在使用LEFT JOIN时放错位置会让结果产生完全不同的语义排查起来非常麻烦。第二个坑忘记DISTINCT。JOIN写法中同一个工程因为多条供应记录而重复这是最隐蔽的错误。光是看前几行结果很容易以为没问题一旦统计工程数量就会比实际多。建议任何通过JOIN筛选“存在性”的查询都先问自己一句这个JOIN会不会让结果集行数膨胀如果会就必须去重。第三个坑把“同时使用”写成OR。用OR表示的是“只要用了其中任意一个就符合”这就把题目的条件从交集扩大成了并集。用这个写法查出来的结果会包含只用了红色螺母、没用到蓝色螺丝刀的工程业务含义完全变了。第四个坑把条件写成同一条记录里的AND。像前面说的一条SPJ记录只对应一种零件在同一行里要求既是红色螺母又是蓝色螺丝刀是绝对矛盾的。如果表的视图里能同时看到零件颜色和名称两列很容易被误导反而忘记了行与行之间的关系。第五个坑子查询里漏掉关联条件。EXISTS写法里面如果忘了写SPJ_RED.JNO J.JNO子查询就变成了一个与外部无关的独立查询只要表里存在任意一个红色螺母记录就会返回所有工程结果必然错误。这类问题往往在数据量小时不容易发现因为有些数据库优化器会做特殊处理但在生产环境很容易爆炸。我的自检方法是把子查询单独拿出来执行看返回的行数和内容是否符合预期再结合外层表去判断。5.3 用GROUP BY和HAVING也能做吗聊到这里你可能已经想到这类问题也可以用GROUP BY和HAVING来写。思路是先找到所有用到了红色螺母或蓝色螺丝刀的工程按工程编号分组然后统计每个组内符合条件的零件种类数最后用HAVING要求种类数等于2。SELECT SPJ.JNO FROM SPJ JOIN P ON SPJ.PNO P.PNO WHERE (P.PNAME 螺母 AND P.COLOR 红) OR (P.PNAME 螺丝刀 AND P.COLOR 蓝) GROUP BY SPJ.JNO HAVING COUNT(DISTINCT P.PNO) 2;这种写法在思路上很巧妙把“同时使用两种零件”转换成了“满足条件的零件种类数等于2”。它用COUNT(DISTINCT P.PNO)来统计避免了同一零件有多条记录导致的数量误判。不过这种写法也有一个前提就是题目中的两种零件它们的PNO必须不同。在本题中红色螺母和蓝色螺丝刀显然是两种不同的零件所以PNO不同用COUNT(DISTINCT P.PNO)没问题。如果两种零件规格相同但颜色不同PNO可能一样那统计方式就要换成COUNT(DISTINCT P.COLOR)或者COUNT(DISTINCT CONCAT(COLOR, PNAME))具体问题要具体分析。GROUP BY方案的优点在于一旦题目条件多到三种、四种零件代码行数也不会线性增长只需要在WHERE里继续加OR条件再把HAVING里的数字改掉就行。缺点是可读性比IN写法稍差而且如果WHERE条件写错或者零件种类数统计口径不对排查难度也比较大。我一般把这种写法作为第三种验证方案跟其他写法交叉核对结果。6. 性能优化与生产环境落地经验6.1 索引设计别让查询输在起跑线上在窄表上跑这种关联查询索引设计直接决定执行计划的好坏。SPJ表作为关系表业务上高频的查询条件无非是供应商编号、零件编号、工程编号以及QTY。这里有一个非常经典的索引建议就是把外键字段分别建上索引。CREATE INDEX idx_spj_jno ON SPJ(JNO); CREATE INDEX idx_spj_pno ON SPJ(PNO); CREATE INDEX idx_spj_sno ON SPJ(SNO);联合主键(SNO, PNO, JNO)本身可以覆盖一些走前缀的查询比如以SNO开头的组合查询。但如果查询经常以JNO或PNO作为过滤条件就必须额外建索引。拿这道题举例EXISTS写法里内层子查询是SPJ_RED.JNO J.JNO AND SPJ_RED.PNO P_RED.PNO如果只靠联合主键而JNO不是左前缀就没法高效定位。所以实际落地时我会针对这种查询把索引建为(JNO, PNO)这样一次索引查找就能同时过滤工程和零件条件。P表方面零件名称和颜色字段如果是经常共同出现的过滤条件可以建一个复合索引(PNAME, COLOR)。虽然这张表通常很小全表扫描代价也不高但在生产环境里零件可能多达几十万行这时一个合适的复合索引就能把嵌套循环连接的成本大幅压下去。6.2 大数据量下的执行计划对比为了了解不同写法的真实性能我在一张模拟数据量较大的环境里跑过三种写法的执行计划。具体数据量是SPJ表约100万行P表约1万行J表约5000行。结果大致如下JOIN写法优化器选择从P表过滤出两个零件然后分别走SPJ表的索引嵌套循环连接。由于两个零件条件过滤后的中间结果集不大整体代价可控。但如果没有在SPJ.JNO上建索引最后还是避免不了对J表或中间结果的排序或哈希操作。IN写法优化器在多数数据库里会把IN子查询改写成半连接SEMI JOIN执行路径和JOIN写法很像性能也不错。但某些优化器不强的数据库里子查询可能被物化成临时表如果子查询结果集很大物化开销就上去了。EXISTS写法相关子查询在SQL Server、Oracle这类数据库里往往会被优化为半连接性能不比JOIN差。在MySQL里具体版本不同优化效果差异也很大。我实测过MySQL 8.0版本EXISTS写法的执行计划常常会变成NESTED LOOP SEMI JOIN配合(JNO, PNO)复合索引单次查询耗时非常稳定。有一个普遍适用的经验是不要凭直觉猜测性能要看执行计划。同一道题在不同数据库、不同数据分布下最优写法可能完全不同。生产环境里我的标准动作是先用EXPLAIN看执行计划找到瓶颈节点再对比调整。只要索引到位三种写法的性能差距通常不会超过一个数量级真正拉开差距的是有没有重点优化“关联字段的索引”。6.3 CTE拆解与临时表方案写复杂查询时我更喜欢用CTE公用表表达式把问题拆得清晰一点。以这道题为例先分别算两个集合再求交集逻辑一目了然。WITH red_nut_jnos AS ( SELECT DISTINCT SPJ.JNO FROM SPJ JOIN P ON SPJ.PNO P.PNO WHERE P.PNAME 螺母 AND P.COLOR 红 ), blue_screwdriver_jnos AS ( SELECT DISTINCT SPJ.JNO FROM SPJ JOIN P ON SPJ.PNO P.PNO WHERE P.PNAME 螺丝刀 AND P.COLOR 蓝 ) SELECT J.JNO, J.JNAME FROM J WHERE J.JNO IN (SELECT JNO FROM red_nut_jnos) AND J.JNO IN (SELECT JNO FROM blue_screwdriver_jnos);CTE的好处是逻辑分层每个集合单独验证排错时可以直接把CTE段落单独执行。在MySQL 8.0、PostgreSQL、SQL Server、Oracle里CTE都是标准功能。如果你的数据库版本太老也可以用临时表替代。实际工作中当我需要为一个临时报表写一条很长的SQL时CTE几乎是我唯一的选择它能极大降低“一眼望不到头”的复杂度。6.4 生产日志里的慢查询排查思路如果这种查询在生产环境变慢了最优先排查的应该是索引和统计信息而不是改写SQL。我遇到过很多次类似场面开发同学说“SQL执行了十几秒”一查执行计划发现某个关键的关联字段没有索引或者索引失效了。加上索引之后同样的SQL瞬间降到几十毫秒。统计信息过期也是慢查询的常见元凶。数据库优化器依赖统计信息估算行数和连接顺序如果统计信息不准可能选错执行计划。遇到这种情况刷新统计信息往往比改SQL更有效。再者如果查询里用到的P表或J表本身有大量历史数据但业务上只需要“当前有效”的数据那就要在关联之前先做一次过滤尽量缩小参与JOIN的数据集。这也是为什么我在前面的SQL里总是尽量提前用WHERE条件把P表缩小到红色螺母或蓝色螺丝刀两类记录而不是先做大的连接再过滤。7. 实操心得与扩展思考7.1 用“分组求交”的思路应对题目变体这类“同时使用多种零件”的题最常见的变体就是增加条件数量。比如变成“查询同时使用P1、P2、P3三种零件的工程”或者变成“查询使用了全部零件的工程”。后一种就是标准的关系除法解决的思路也比单一题要更系统。对于确定数量的零件组合用IN子查询叠加是最稳的每增加一个条件就多加一个IN。但如果零件数量不确定或者需要判断“是否覆盖某个零件集合”分组计数法更合适。把目标零件集合先限定出来按JNO分组用COUNT(DISTINCT P.PNO)统计满足条件的零件种类数再和目标种类数比较。这个思路在面试里经常被追问实际业务里做套餐组合校验时也能用上。7.2 自检SQL的四个习惯最后分享几个我在工作中一直在用的自检习惯。第一个习惯任何涉及存在性的查询都先想清楚“结果集行数”会不会膨胀。第二个习惯至少用两种写法执行同一需求对比结果集是否完全一致。第三个习惯子查询单独执行验证先看子查询的返回内容再组合外层逻辑。第四个习惯看执行计划尤其是关联字段的索引有没有被正确使用。这四点基本能覆盖绝大多数SQL逻辑问题和性能隐患。回到10-94这道题本身它的价值不在于题目有多难而在于它把“集合思维”真正落到了SQL语法层面。能用JOIN写出结果说明你掌握了表连接能用IN或EXISTS写出结果说明你理解了集合语义能解释清楚三种写法的差异和性能取舍说明你对数据库执行机制有了自己的判断。这道题我从第一次见到现在每次回头看都会有新的体会也推荐你把文中的几种写法实际跑一遍感受一下不同写法的执行计划差异这比单纯背答案要有用得多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。