-- 15、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

此SQL查询从学生表和分数表中联合数据,找出所有至少有两门课程成绩不及格(低于60分或未记录成绩)的学生,计算他们的平均分数。这有助于分析学生的学习表现。
SELECT 
st.s_id,st.s_name,AVG(sc.s_score) 
FROM student st
LEFT JOIN score sc ON sc.s_id=st.s_id
WHERE sc.s_id IN
(
SELECT sc.s_id 
FROM score sc 
WHERE sc.s_score<60 OR sc.s_score IS NULL
GROUP BY sc.s_id HAVING COUNT(1)>=2
)
GROUP BY st.s_id
<think> 我们有两个表:student(学生表)和score(成绩表)。需要查询两门及以上不及格课程(成绩<60)的学生学号姓名平均成绩(包括所有课程平均成绩)。 注意:平均成绩是所有课程的平均,而不仅仅是不及格课程。 步骤: 1. 首先,我们需要找出每个学生不及格课程数。可以通过成绩表score进行分组(按学生学号),然后统计每个学生不及格课程的数量。 2. 然后,我们只需要不及格课程数>=2的学生。 3. 同时,我们需要计算这些学生平均成绩(所有课程平均成绩,包括及格和不及格)。 4. 最后,关联学生表获取学生姓名。 注意:在Hive中,我们使用HQL。 参考用户之前的查询,但注意用户要求的是有两门以上不及格课程,但平均成绩是指该学生所有课程平均成绩(不是不及格课程的平均)。 我们可以先通过score表计算每个学生不及格课程数量,同时计算该学生的所有课程平均成绩(或者分别计算也可以,但是注意条件是不及格课程数量>=2)。 方案: 1. 先对score表进行分组,计算每个学生不及格课程数(count(course_id) where score<60)和总平均成绩(avg(score))。 但是注意:这里的不及格课程数需要单独计算,因为条件只针对不及格课程,而平均成绩是所有课程的。 2. 所以我们可以先过滤出不及格的成绩,然后按学号分组统计不及格课程数。同时,我们还需要计算每个学生的所有课程平均成绩(这需要原始的score表)。 因此,我们可以分成两个子查询: a. 计算每个学生不及格课程数(按学号分组,条件为score<60) b. 计算每个学生的所有课程平均成绩(按学号分组) 然后关联这两个子查询,筛选出不及格课程数>=2的学生,再关联学生表获取姓名。 另一种做法:先计算每个学生的所有课程平均成绩(总平均),然后计算每个学生不及格课程数(通过case when或者子查询),然后筛选。 这里我们采用两个子查询然后关联的方式,这样逻辑清晰。 具体步骤: 步骤1:统计每个学生不及格课程数 select s_id, count(1) as fail_count from score where score < 60 group by s_id having fail_count >= 2 -- 这里可以直接过滤掉不及格课程数<2的 步骤2:统计每个学生平均成绩(所有课程) select s_id, avg(score) as avg_score from score group by s_id 然后将步骤1的结果与步骤2的结果按学号关联,再关联学生表。 注意:可能有学生没有不及格课程,但步骤1已经过滤了不及格课程数<2的,所以步骤1中只包含有2门及以上不及格学生。 但是步骤2中包含了所有有成绩的学生(没有成绩的学生可能不在步骤2中,但题目要求是查询不及格课程同学,所以没有成绩的学生自然也没有不及格课程,因此不在我们的结果中)。 因此,我们可以这样写: select stu.s_id, stu.s_name, t2.avg_score from student stu join ( -- 步骤2:计算每个学生平均成绩 select s_id, avg(score) as avg_score from score group by s_id ) t2 on stu.s_id = t2.s_id join ( -- 步骤1:有两门及以上不及格课程学生学号 select s_id, count(1) as fail_count from score where score < 60 group by s_id having count(1) >= 2 ) t1 on stu.s_id = t1.s_id 但是,这样写可能会漏掉一种情况:如果某个学生在步骤1中(即有两门以上不及格)但是在步骤2中没有记录(即没有成绩)?不可能,因为成绩表score中只要有不及格成绩,那么该学生肯定有成绩记录,所以步骤2中会有记录。 另一种写法:在一个子查询中同时计算总平均成绩不及格课程数,这样可以避免一次join,但是需要两次扫描score表吗?其实不用,我们可以用一次扫描同时计算两个指标: select s_id, avg(score) as avg_score, sum(if(score<60,1,0)) as fail_count -- 或者用count(if(score<60,1,null)) from score group by s_id having fail_count >= 2 这样更简洁!所以我们可以这样写: select stu.s_id, stu.s_name, t1.avg_score from student stu join ( select s_id, avg(score) as avg_score, sum(if(score<60,1,0)) as fail_count -- 使用聚合函数计算不及格门数 from score group by s_id having fail_count >= 2 -- 注意:having子句中可以使用别名,也可以直接使用表达式 ) t1 on stu.s_id = t1.s_id 但是,这里需要注意:having子句在Hive中可以使用别名(取决于Hive版本),也可以直接使用表达式: having sum(if(score<60,1,0)) >= 2 这样写更通用。 因此,最终的HQL语句如下: </think> 以下是针对您需求的 HQL 查询实现,结合了学生表(`student`)和成绩表(`score`),查询**有两门及以上不及格课程(成绩<60分)** 的学生学号姓名平均成绩: ```sql SELECT s.s_id AS student_id, s.s_name AS student_name, ROUND(AVG(sc.s_score), 2) AS avg_score FROM student s JOIN score sc ON s.s_id = sc.s_id WHERE sc.s_id IN ( --查询:筛选不及格科目≥2的学生 SELECT s_id FROM score WHERE s_score < 60 GROUP BY s_id HAVING COUNT(1) >= 2 -- 统计不及格科目数量[^1] ) GROUP BY s.s_id, s.s_name; ``` ### 关键解析: 1. **子查询定位目标学生** ```sql SELECT s_id FROM score WHERE s_score < 60 -- 过滤不及格成绩 GROUP BY s_id HAVING COUNT(1) >= 2 -- 统计不及格科目≥2[^1] ``` 通过子查询高效筛选出有两门及以上不及格课程学号列表。 2. **主查询计算平均成绩** ```sql ROUND(AVG(sc.s_score), 2) -- 计算所有课程平均成绩(含及格科目) ``` 对所有科目成绩取平均(不仅是过滤后的不及格科目),保留两位小数[^3]。 3. **表连接与分组** ```sql JOIN score sc ON s.s_id = sc.s_id GROUP BY s.s_id, s.s_name --学生维度聚合 ``` 通过内连接确保只返回有成绩记录的学生,并按学号+姓名分组保证结果唯一性。 ### 注意事项: 1. **性能优化** 如果成绩表数据量大,建议在子查询的 `WHERE s_score < 60` 条件列添加索引。 2. **空值处理** 若存在无成绩记录的学生需要包含,可将 `JOIN` 改为 `LEFT JOIN` 并在主查询添加: ```sql HAVING avg_score IS NOT NULL -- 排除无成绩记录 ``` 3. **结果验证** 可与原始SQL逻辑对比验证结果一致性: ```sql -- 引用[1]的原始逻辑(Oracle语法) SELECT a.s_id, a.s_name, ROUND(AVG(b.s_score),2) FROM student a JOIN score b ON a.s_id = b.s_id AND b.s_score < 60 GROUP BY a.s_id, a.s_name HAVING COUNT(1) >= 2; ``` ### 执行结果示例: | student_id | student_name | avg_score | |------------|--------------|-----------| | S001 | 张三 | 52.75 | | S005 | 李四 | 48.30 | > 结果显示张三有2门不及格科目且所有课程平均分52.75,李四有3门不及格科目且平均分48.30。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值