高阶子查询题目精炼

数据库子查询题集(10题)

以下题目基于典型的"学生-课程-成绩"数据库模式,并引入复杂场景、性能陷阱及高级SQL特性,旨在深入考察对子查询执行原理、优化策略及逻辑转换的理解。

题号 题目描述 考察核心
1 多层嵌套与聚合:查询"平均成绩高于全校学生平均成绩,且其最高单科成绩高于该科目平均分20分以上"的学生的学号和姓名。 多层聚合子查询、HAVING子句中的复杂条件。
2 相关子查询与极值 :找出每门课程中,成绩不是最高也不是最低的学生名单(即排除每门课的最高分和最低分获得者)。 相关子查询、NOT IN与极值查询(MAX, MIN)的结合。
3 EXISTS 与 三表关联:查询从未选修过"张老师"所授课程,但选修过所有"李老师"所授课程的学生信息。 EXISTS, NOT EXISTS 与全称量词的逻辑转换(通过双重否定或计数实现)。
4 ALL/ANY 与除法运算 :查询选修了计算机系 开设的全部课程的学生学号。 使用 NOT EXISTS 模拟关系代数中的"除法"运算。
5 标量子查询与更新 :将每位学生的总成绩更新到学生表的一个新增字段total_score中,要求使用单条UPDATE语句配合标量子查询完成。 UPDATESET子句中使用相关标量子查询。
6 FROM 子句中的子查询与窗口函数:查询每个班级中,总成绩排名前10%的学生。要求使用FROM子句中的子查询先计算排名和百分比,再筛选。 子查询生成派生表(Derived Table)并与窗口函数(RANK(), NTILE())结合。
7 条件子查询与 CASE 表达式:生成一份报告,列出所有学生。对于每个学生,如果其有任何一门课成绩低于60分,则显示"有不及格";如果所有成绩都在85分以上,则显示"优秀";否则显示"普通"。 SELECT子句和CASE WHEN中使用EXISTS和聚合子查询。
8 横向连接 (LATERAL JOIN) 问题:对于每个学生,找出其成绩最高的两门课程的名称和分数。 使用 LATERAL 子查询(或特定数据库的等效语法)实现行间关联。
9 集合运算与子查询性能 :查询本学期有成绩记录,但上学年没有任何成绩记录的学生。分别用NOT INNOT EXISTSLEFT JOIN...IS NULL三种方法实现,并分析其执行计划可能存在的差异。 不同逻辑写法对数据库优化器(如He3DB的子连接上拉)的影响及性能对比。
10 递归子查询(CTE) :给定一个"员工-经理"表(employee_id, manager_id, salary),找出每个员工的所有间接下属(不限层级)中,薪资最高的那位员工的薪资。 使用递归公共表表达式(CTE)遍历树形结构,并在外层查询中进行聚合。

部分难题的参考解答与深度解析

题目 2:相关子查询与极值

sql 复制代码
-- 方法:找出每门课成绩既不是最大值也不是最小值的学生
SELECT DISTINCT s.student_id, s.name, c.course_name, sc.score
FROM student s
JOIN score sc ON s.student_id = sc.student_id
JOIN course c ON sc.course_id = c.course_id
WHERE sc.score <> (
    SELECT MAX(score) FROM score sc2 
    WHERE sc2.course_id = sc.course_id -- 相关子查询,找当前课程最高分
)
AND sc.score <> (
    SELECT MIN(score) FROM score sc3 
    WHERE sc3.course_id = sc.course_id -- 相关子查询,找当前课程最低分
);

解析 :此查询为每个成绩行执行了两次相关子查询以获取当前课程的极值,可能产生性能问题。优化器可能尝试将其转换为JOIN,但相关性子查询限制了上拉优化。

题目 4:ALL/ANY 与除法运算(使用 NOT EXISTS 实现)

sql 复制代码
-- 查询选修了计算机系全部课程的学生
SELECT s.student_id
FROM student s
WHERE NOT EXISTS (
    -- 找出计算机系开设的、该学生没选的课程
    SELECT c.course_id 
    FROM course c 
    WHERE c.dept = '计算机系'
    AND NOT EXISTS (
        SELECT 1 
        FROM score sc 
        WHERE sc.student_id = s.student_id 
        AND sc.course_id = c.course_id
    )
);

解析 :这是经典的关系除法问题的NOT EXISTS解法。它通过双重否定("不存在一门计算机系的课是他没选的")来表达全称量词。外层查询遍历学生,内层第一重NOT EXISTS检查是否有一门计算机系的课不在该学生的选课列表中(由最内层NOT EXISTS判断)。这种嵌套相关子查询是优化器的重点优化对象,在He3DB等数据库中可能会被重写为更高效的semijoinantijoin

题目 9:集合运算与性能分析

sql 复制代码
-- 方法1: NOT IN (需注意NULL值问题)
SELECT student_id FROM score WHERE term = '2024-春'
AND student_id NOT IN (
    SELECT student_id FROM score WHERE term = '2023-秋'
);

-- 方法2: NOT EXISTS (通常更优)
SELECT s.student_id 
FROM score s 
WHERE s.term = '2024-春'
AND NOT EXISTS (
    SELECT 1 FROM score s2 
    WHERE s2.term = '2023-秋' 
    AND s2.student_id = s.student_id
);

-- 方法3: LEFT JOIN ... IS NULL
SELECT DISTINCT s.student_id
FROM score s 
LEFT JOIN score s_prev ON s.student_id = s_prev.student_id AND s_prev.term = '2023-秋'
WHERE s.term = '2024-春' 
AND s_prev.student_id IS NULL;

性能分析

  • NOT IN :如果子查询返回结果集包含NULL值,则整个NOT IN条件可能返回UNKNOWN(即无结果),这是逻辑陷阱。性能上,数据库可能将其实现为Hash Anti Join
  • NOT EXISTS :是表达此逻辑最清晰和安全的方式,通常能利用关联字段的索引。现代数据库优化器(如MySQL 8.0+、He3DB)擅长将NOT EXISTS重写为高效的antijoin
  • LEFT JOIN...IS NULL :这是一种更"过程化"的写法,明确指示数据库先进行连接操作。在拥有良好统计信息时,优化器可能生成与NOT EXISTS相似的执行计划。但在复杂情况下,它可能比NOT EXISTS产生更大的中间结果集。

核心优化点 :数据库优化器在查询重写阶段,会尝试将子查询(特别是EXISTS/IN类)"上拉"为JOIN,以减少嵌套迭代。例如,He3DB会将满足条件的EXISTS_SUBLINK转换为semijoin,将NOT EXISTS转换为antijoin,从而可以利用高效的连接算法(如Hash Join)和并行执行。能否成功上拉取决于子查询是否"相关"(引用外层列)以及其复杂程度。

题目 10:递归子查询(CTE)

sql 复制代码
WITH RECURSIVE SubordinateTree AS (
    -- 锚点:直接下属
    SELECT employee_id, manager_id, salary
    FROM employee
    UNION ALL
    -- 递归:间接下属
    SELECT st.employee_id, e.manager_id, e.salary
    FROM SubordinateTree st
    JOIN employee e ON st.manager_id = e.employee_id
)
SELECT 
    emp.employee_id,
    (SELECT MAX(salary) FROM SubordinateTree st WHERE st.employee_id = emp.employee_id) as max_subordinate_salary
FROM employee emp;

解析:此CTE先递归构建出每个员工的所有下属关系(包括间接)。外层查询通过一个标量子查询,从递归结果中找出每个员工下属的最高薪资。递归CTE是解决层次查询的强有力工具,但其性能依赖于递归深度和表的索引情况。


参考来源

相关推荐
身如柳絮随风扬2 小时前
数据库读写分离:从原理到实战,构建高并发系统
数据库·mysql
风之所往_2 小时前
Python 3.4 新特性全面总结
python
雪度娃娃2 小时前
转向现代C++——在意为改写的函数添加 override
开发语言·c++
太阳上的雨天2 小时前
任何格式的文件转Markdown
python·ai
提笔了无痕3 小时前
RAG存储策略中.md格式的切片与存储怎么处理
数据库·ai·rag
yaoxin5211233 小时前
419. 现代 Java IO 最佳实践 - 写入文本文件
java·windows·python
陳土3 小时前
DuckDB精读——基于Getting started with DuckDB
数据库·oracle
喵星人工作室3 小时前
C++火影忍者1.1.2
开发语言·c++