004 MySQL练习

选课系统中存在这样的三种表

学生表(Student): 学号(S#)、学生姓名(Sname)、学生年龄(Sage)、学生性别(Ssex);

课程表(Course):课程编号(C#)、课程名称(Cname)、教师编号(T#);

成绩表(SC):学号(S#)、课程编号(C#)、成绩(score);

教师表(Teacher):教师编号(T#)、教师名字(Tname)。

请用SQL完成以下需求

a查询"001"课程比"002"课程成绩高的所有学生的学号

b查询平均成绩大于60分的同学的学号和平均成绩

c查询所有同学的学号、姓名、选课数、总成绩

d查询没有学过"叶平"老师的同学的学号、姓名 e查询学过"001"并且也学过编号"002"课程的同学的学号、姓名

首先,我会针对每个需求分别给出SQL查询语句。

a) 查询"001"课程比"002"课程成绩高的所有学生的学号

sql 复制代码
SELECT s1.S#  
FROM SC s1, SC s2  
WHERE s1.S# = s2.S#  
  AND s1.C# = '001'  
  AND s2.C# = '002'  
  AND s1.score > s2.score;



-- 当你执行这个 SQL 查询时,数据库管理系统(DBMS)会遵循一定的逻辑和执行顺序来处理查询。虽然实际的物理执行顺序可能因数据库的查询优化器而异,但我们可以按照逻辑执行顺序来解释这个查询。以下是该查询的逻辑执行顺序和讲解:

-- FROM 子句:
-- 首先,DBMS 会确定要从哪些表中检索数据。在这个查询中,我们有两个表实例 SC s1 和 SC s2,它们实际上是同一个表 SC 的两个别名。使用别名允许我们在查询中多次引用同一个表,这在进行比较或连接表的不同行时非常有用。
-- WHERE 子句(包括连接条件):
-- 接下来,DBMS 会应用 WHERE 子句中的条件来过滤记录。这个查询的 WHERE 子句包含几个条件,它们将一起应用:
-- s1.S# = s2.S#:这是一个连接条件,用于将 s1 和 s2 两个表实例中的记录匹配起来。具体来说,它会找出那些在两个表实例中具有相同学号 (S#) 的记录。这实际上是一种自连接,因为我们正在比较同一个表的不同行。
-- s1.C# = '001' 和 s2.C# = '002':这两个条件分别过滤出 s1 中课程编号为 '001' 的记录和 s2 中课程编号为 '002' 的记录。
-- s1.score > s2.score:这个条件比较了匹配学号在两个不同课程中的分数,只选择那些在 '001' 课程中分数高于 '002' 课程的记录。
-- SELECT 子句:
-- 最后,一旦所有符合条件的记录都被确定,DBMS 就会根据 SELECT 子句中的指示选择并返回特定的列。在这个查询中,我们仅选择并返回了 s1.S#,即满足所有上述条件的学生的学号。
-- 总结来说,这个查询的逻辑是找出那些在 '001' 课程中得分高于 '002' 课程的所有学生的学号。它通过比较同一个学生(由学号 S# 标识)在两门不同课程中的分数来实现这一点。这种查询在处理诸如成绩比较、趋势分析或识别特定表现模式的学生等任务时非常有用。

b) 查询平均成绩大于60分的同学的学号和平均成绩

sql 复制代码
SELECT S#, AVG(score) AS avg_score  
FROM SC  
GROUP BY S#  
HAVING AVG(score) > 60;

c) 查询所有同学的学号、姓名、选课数、总成绩

sql 复制代码
SELECT Student.S#, Student.Sname, COUNT(SC.C#) AS course_count, SUM(SC.score) AS total_score  
FROM Student  
LEFT JOIN SC ON Student.S# = SC.S#  
GROUP BY Student.S#, Student.Sname;

d) 查询没有学过"叶平"老师的同学的学号、姓名

sql 复制代码
SELECT Student.S#, Student.Sname  
FROM Student  
WHERE S# NOT IN (  
    SELECT DISTINCT SC.S#  
    FROM SC  
    JOIN Course ON SC.C# = Course.C#  
    JOIN Teacher ON Course.T# = Teacher.T#  
    WHERE Teacher.Tname = '叶平'  
);


-- 这条SQL查询的目的是从Student表中选择那些没有选修过'叶平'老师课程的学生的学号和姓名。下面我将从执行顺序的角度详细解释这条查询。

-- 查询解释
-- 内层子查询:
-- sql
-- SELECT DISTINCT SC.S#    
-- FROM SC    
-- JOIN Course ON SC.C# = Course.C#    
-- JOIN Teacher ON Course.T# = Teacher.T#    
-- WHERE Teacher.Tname = '叶平'
-- 这个子查询首先通过JOIN操作连接了三个表:SC(学生课程表)、Course(课程表)和Teacher(教师表)。连接条件是课程编号(C#)和教师编号(T#)。然后,它筛选出教师名字为'叶平'的课程所对应的学生编号(S#),并确保学生编号是唯一的(通过DISTINCT)。

-- 外层查询:
-- sql
-- SELECT Student.S#, Student.Sname    
-- FROM Student    
-- WHERE S# NOT IN (子查询结果)
-- 外层查询从Student表中选择学号(S#)和姓名(Sname)。WHERE子句中的条件是,学生的学号不能出现在内层子查询的结果中。换句话说,这个查询选择了所有没有选修过'叶平'老师课程的学生。

-- 执行顺序(逻辑上)
-- FROM 子句:首先确定查询的主体表,这里是Student表。
-- 内层子查询:在执行外层查询之前,数据库会先执行内层子查询。
-- JOIN操作:先连接SC、Course和Teacher三个表,基于课程编号和教师编号的匹配。
-- WHERE子句:从连接后的结果中筛选出教师名字为'叶平'的记录。
-- DISTINCT:确保返回的学生编号是唯一的。
-- WHERE 子句(外层查询):使用内层子查询的结果来过滤Student表中的记录。具体来说,它会排除那些学号出现在内层子查询结果中的学生。
-- SELECT 子句:最后,从经过过滤的Student表中选择学号和姓名。
-- 这条查询的逻辑执行顺序大致如上所述,但实际的物理执行顺序可能会因数据库的查询优化器而有所调整,以提高查询效率。不过,从逻辑上理解,这个顺序有助于我们更好地掌握查询的工作原理。
sql 复制代码
SELECT s.S#, s.Sname  
FROM Student s  
LEFT JOIN (  
    SELECT DISTINCT sc.S#  
    FROM SC sc  
    JOIN Course c ON sc.C# = c.C#  
    JOIN Teacher t ON c.T# = t.T#  
    WHERE t.Tname = '叶平'  
) AS taught_by_yeping ON s.S# = taught_by_yeping.S#  
WHERE taught_by_yeping.S# IS NULL;


-- 在这个查询中,我们首先创建了一个子查询(别名为 taught_by_yeping),该子查询选出了所有学过"叶平"老师课程的学生的学号。然后,我们使用 LEFT JOIN 将这个子查询的结果连接到 Student 表上。最后,通过 WHERE 子句筛选出那些在子查询结果中没有对应学号的学生,即没有学过"叶平"老师课程的学生。

-- 这种方法通常比使用 NOT IN 更高效,特别是在处理大量数据时,因为 LEFT JOIN 通常能够更好地利用数据库索引,从而减少查询的执行时间。


-- 子查询执行:
-- 在执行主查询之前,MySQL会首先处理子查询部分。这个子查询用于创建一个临时表(在本查询中命名为taught_by_yeping),其中包含选修了'叶平'老师课程的学生的学号(S#)。

-- sql
-- SELECT DISTINCT sc.S#    
-- FROM SC sc    
-- JOIN Course c ON sc.C# = c.C#    
-- JOIN Teacher t ON c.T# = t.T#    
-- WHERE t.Tname = '叶平'
-- 首先,SC表(别名sc)与Course表(别名c)通过课程编号(C#)进行连接。
-- 接着,连接后的结果与Teacher表(别名t)通过教师编号(T#)进行连接。
-- 然后,WHERE子句筛选出教师名字为'叶平'的记录。
-- 最后,使用DISTINCT关键字确保返回的学生学号(S#)是唯一的。
-- 这个子查询的结果集将作为一个临时表taught_by_yeping,包含所有选修了'叶平'老师课程的学生的学号。

-- 主查询执行:
-- 在子查询执行完毕后,主查询开始执行。主查询的目的是从Student表中选择那些没有选修过'叶平'老师课程的学生的学号和姓名。

-- sql
-- SELECT s.S#, s.Sname    
-- FROM Student s    
-- LEFT JOIN taught_by_yeping ON s.S# = taught_by_yeping.S#    
-- WHERE taught_by_yeping.S# IS NULL
-- 首先,Student表(别名s)与前面创建的临时表taught_by_yeping进行左连接(LEFT JOIN),连接条件是学生学号(S#)。
-- 左连接会返回Student表中的所有记录,以及与之匹配的taught_by_yeping表中的记录(如果有的话)。如果taught_by_yeping表中没有匹配的记录,则对应的字段将为NULL。
-- 接着,WHERE子句筛选出taught_by_yeping.S#为NULL的记录,即那些在taught_by_yeping临时表中没有匹配学号的学生,这表示他们没有选修过'叶平'老师的课程。
-- 最后,SELECT子句选择这些学生的学号(S#)和姓名(Sname)。
-- 总结来说,这个查询通过子查询找出选修了'叶平'老师课程的学生学号,然后通过左连接和筛选条件找出没有选修过这些课程的学生,最终返回他们的学号和姓名。
sql 复制代码
SELECT s.S#, s.Sname  
FROM Student s  
LEFT JOIN SC sc ON s.S# = sc.S#  
LEFT JOIN Course c ON sc.C# = c.C#  
LEFT JOIN Teacher t ON c.T# = t.T# AND t.Tname = '叶平'  
GROUP BY s.S#, s.Sname  
HAVING COUNT(DISTINCT t.T#) = 0;



-- 在这个查询中:

-- 我们首先对 Student 表进行左连接,通过 SC 表连接到 Course 表,再通过 Course 表连接到 Teacher 表。
-- 在连接 Teacher 表时,我们在 ON 子句中加入了条件 t.Tname = '叶平',这样只有当教师名字是'叶平'时,才会进行连接。
-- 使用 GROUP BY 对学生学号和姓名进行分组,因为我们想要得到每个学生的结果。
-- HAVING COUNT(DISTINCT t.T#) = 0 确保我们只选择那些没有与'叶平'老师相关联的记录的学生。如果一个学生与'叶平'老师有关联,那么 COUNT(DISTINCT t.T#) 的值会大于0。
-- 请注意,这种方法可能在性能上不如使用子查询的方法,特别是当数据库中的记录数量非常大时。在实际应用中,建议测试不同的查询方法,以确定哪种方法在您的特定数据库和数据量下最有效。

-- 另外,如果数据库中存在大量数据,确保相关的字段(如学号、课程号和教师号)已经被正确地索引,以提高查询性能。

e) 查询学过"001"并且也学过编号"002"课程的同学的学号、姓名

sql 复制代码
SELECT Student.S#, Student.Sname  
FROM Student  
WHERE S# IN (  
    SELECT S# FROM SC WHERE C# = '001'  
) AND S# IN (  
    SELECT S# FROM SC WHERE C# = '002'  
);

或者使用EXISTS子句来实现:

sql 复制代码
SELECT s.S#, s.Sname  
FROM Student s  
WHERE EXISTS (SELECT 1 FROM SC WHERE S# = s.S# AND C# = '001')  
  AND EXISTS (SELECT 1 FROM SC WHERE S# = s.S# AND C# = '002');

请注意,这些查询假设数据库中的表和字段都严格遵循您提供的结构,并且所有的字段名和表名都是准确无误的。在实际应用中,您可能需要根据实际的数据库结构和命名来调整这些查询。

相关推荐
皓空揽月27 分钟前
Win10安装MongoDB(详细版)
数据库·mongodb
源代码:趴菜27 分钟前
MySQL表操作
数据库·mysql
字节跳动数据平台34 分钟前
深耕分析型数据库领域,火山引擎ByteHouse入围《2024爱分析数据库厂商全景报告
数据库·后端·云原生
mosi35734 分钟前
使用C#进行MySQL删改查操作
mysql·c#
续亮~36 分钟前
6、Redis系统-数据结构-07-QuickList
数据结构·数据库·redis
想做后端的前端43 分钟前
Redis的持久化机制
数据库·redis·bootstrap
李长安的博客1 小时前
Oracle PL / SQL update更新数据
数据库·sql·oracle
生活真难1 小时前
Postgresql - 用户权限数据库
数据库
韩楚风1 小时前
【手写数据库内核组件】0201 哈希表hashtable的实战演练,多种非加密算法,hash桶的冲突处理,查找插入删除操作的代码实现
c语言·数据结构·数据库·哈希算法·散列表
☀️1 小时前
Redis 的过期策略
数据库·redis·缓存