题目4:MySQL----------Rank Scores

本文介绍如何使用SQL查询实现动态排名,解决分数相同情况下的并列排名问题,确保排名序列连续无空缺。

Write a SQL query to rank scores. If there is a tie between two scores, both should have the same ranking. Note that after a tie, the next ranking number should be the next consecutive integer value. In other words, there should be no "holes" between ranks.

+----+-------+
| Id | Score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+

For example, given the above Scores table, your query should generate the following report (order by highest score):

+-------+------+
| Score | Rank |
+-------+------+
| 4.00  | 1    |
| 4.00  | 1    |
| 3.85  | 2    |
| 3.65  | 3    |
| 3.65  | 3    |
| 3.50  | 4    |
+-------+------+

题目解答:

一、

SELECT
  Score,
  (SELECT count(*) FROM (SELECT distinct Score s FROM Scores) tem WHERE s >= Score) Rank
FROM Scores
ORDER BY Score desc;

二、

# Write your MySQL query statement below
SELECT Scores.Score, COUNT(Ranking.Score) AS RANK
  FROM Scores
     , (
       SELECT DISTINCT Score
         FROM Scores
       ) Ranking
 WHERE Scores.Score <= Ranking.Score
 GROUP BY Scores.Id, Scores.Score
 ORDER BY Scores.Score DESC;




<think>根据用户需求,我们需要在MySQL中创建四个函数。基于之前的表结构假设和需求描述,我们将编写以下函数: 1. **根据学号查询学生成绩等级**:计算该学生所有课程的平均分,然后根据平均分返回等级(优秀、良好、中等、及格、不及格)。 2. **根据课程号查询本课程平均分**:直接计算并返回该课程的平均分。 3. **根据学号和课程号查询学生是否选了本课程**:返回一个布尔值(在MySQL中实际上是整数1或0)表示该学生是否选择了该课程。 4. **根据学号和课程号返回该学生的本课程排名**:使用窗口函数(MySQL 8.0+)计算该学生在该课程中的排名(这里使用DENSE_RANK,避免并列排名后名次不连续)。 需要注意的是,我们假设成绩表中存储的是数值型的分数,且可能为NULL。在计算平均分时,AVG函数会自动忽略NULL值。但是,如果学生没有成绩(即平均分为NULL),则第一个函数需要处理NULL的情况,这里我们将其视为0分(不及格)?或者按照实际业务逻辑,如果学生没有成绩,应该如何处理?根据需求,我们可以在函数中加入判断:如果平均分为NULL,则返回'不及格'。 同样,排名函数中,如果该学生没有选修该课程,我们将返回NULL。 下面我们逐一创建这些函数。 ### 1. 根据学号查询成绩等级函数 ```sql DELIMITER $$ CREATE FUNCTION GetStudentGradeLevel(stu_id INT) RETURNS VARCHAR(10) DETERMINISTIC BEGIN DECLARE avg_score DECIMAL(5,2); -- 获取平均分,如果学生没有成绩则返回NULL SELECT AVG(score) INTO avg_score FROM scores WHERE student_id = stu_id; -- 如果平均分为NULL,则视为0分(或者根据需求,也可以返回'无成绩',但题目要求60以下不及格,所以返回不及格) IF avg_score IS NULL THEN RETURN '不及格'; END IF; -- 根据平均分返回等级 RETURN CASE WHEN avg_score >= 90 THEN '优秀' WHEN avg_score >= 80 THEN '良好' WHEN avg_score >= 70 THEN '中等' WHEN avg_score >= 60 THEN '及格' ELSE '不及格' END; END$$ DELIMITER ; ``` ### 2. 根据课程号查询本课程平均分 ```sql DELIMITER $$ CREATE FUNCTION GetCourseAverage(c_id INT) RETURNS DECIMAL(5,2) DETERMINISTIC BEGIN DECLARE course_avg DECIMAL(5,2); SELECT AVG(score) INTO course_avg FROM scores WHERE course_id = c_id; -- 如果没有学生选修该课程,则返回NULL RETURN course_avg; END$$ DELIMITER ; ``` ### 3. 根据学号和课程号查询学生是否选了本课程 ```sql DELIMITER $$ CREATE FUNCTION CheckCourseSelection(stu_id INT, c_id INT) RETURNS BOOLEAN DETERMINISTIC BEGIN DECLARE selected BOOLEAN; SELECT EXISTS( SELECT 1 FROM scores WHERE student_id = stu_id AND course_id = c_id ) INTO selected; RETURN selected; END$$ DELIMITER ; ``` ### 4. 根据学号和课程号返回该学生的本课程排名 注意:这个函数在MySQL 5.7以下版本无法使用,因为使用了窗口函数。如果使用MySQL 8.0+,可以如下创建: ```sql DELIMITER $$ CREATE FUNCTION GetCourseRank(stu_id INT, c_id INT) RETURNS INT DETERMINISTIC BEGIN DECLARE s_rank INT; -- 使用子查询和窗口函数计算排名 SELECT `rank` INTO s_rank FROM ( SELECT student_id, DENSE_RANK() OVER (ORDER BY score DESC) AS `rank` FROM scores WHERE course_id = c_id ) AS ranked WHERE student_id = stu_id; -- 如果没有选修,则返回NULL RETURN s_rank; END$$ DELIMITER ; ``` ### 注意事项: 1. 这些函数都使用了`DETERMINISTIC`,表示对于相同的输入总是返回相同的结果。 2. 在调用函数之前,请确保已经创建了相应的表,并且有数据。 3. 函数中使用到的表`scores`需要有合适的索引以提高性能。 4. 如果使用MySQL 5.7或更低版本,第四个函数(排名)无法使用窗口函数,需要重写。一种替代方法是使用变量模拟排名,但比较复杂。 ### 相关问题 1.MySQL中创建函数时,如何处理可能出现的除零错误? 2. 如果课程成绩中有NULL值,对平均分的计算会有什么影响? 3. 在排名函数中,如果两个学生分数相同,使用`DENSE_RANK`和`RANK`有什么区别? 4. 如何优化这些函数在大数据量下的性能? [^1]: 参考了成绩等级的划分标准,根据平均分判断等级。 [^2]: 使用了CASE语句进行多条件判断。 [^3]: 使用了窗口函数进行排名计算。 </think>### MySQL函数实现方案 在MySQL中创建所需函数,基于标准表结构(学生表、课程表、成绩表)实现需求: #### 1. 根据学号查询成绩等级函数 ```sql DELIMITER $$ CREATE FUNCTION GetGradeLevel(stu_id INT) RETURNS VARCHAR(10) DETERMINISTIC BEGIN DECLARE avg_score DECIMAL(5,2); -- 获取学生所有课程平均分 SELECT AVG(score) INTO avg_score FROM scores WHERE student_id = stu_id; -- 分数等级判断逻辑 RETURN CASE WHEN avg_score >= 90 THEN '优秀' WHEN avg_score >= 80 THEN '良好' WHEN avg_score >= 70 THEN '中等' WHEN avg_score >= 60 THEN '及格' ELSE '不及格' END; END$$ DELIMITER ; ``` **使用示例**: ```sql SELECT GetGradeLevel(2023001); -- 返回 '良好' ``` #### 2. 根据课程号查询课程平均分函数 ```sql DELIMITER $$ CREATE FUNCTION GetCourseAverage(course_id INT) RETURNS DECIMAL(5,2) DETERMINISTIC BEGIN DECLARE course_avg DECIMAL(5,2); -- 计算指定课程平均分 SELECT AVG(score) INTO course_avg FROM scores WHERE course_id = course_id; RETURN course_avg; END$$ DELIMITER ; ``` **使用示例**: ```sql SELECT GetCourseAverage('CS101'); -- 返回 85.67 ``` #### 3. 根据学号和课程号查询是否选课函数 ```sql DELIMITER $$ CREATE FUNCTION CheckCourseSelection( stu_id INT, course_id INT ) RETURNS TINYINT(1) DETERMINISTIC BEGIN DECLARE is_selected TINYINT(1); -- 检查选课记录是否存在 SELECT EXISTS( SELECT 1 FROM scores WHERE student_id = stu_id AND course_id = course_id ) INTO is_selected; RETURN is_selected; END$$ DELIMITER ; ``` **使用示例**: ```sql SELECT CheckCourseSelection(2023001, 'CS101'); -- 返回 1(已选)或0(未选) ``` #### 4. 根据学号和课程号查询课程排名函数 ```sql DELIMITER $$ CREATE FUNCTION GetCourseRank( stu_id INT, course_id INT ) RETURNS INT DETERMINISTIC BEGIN DECLARE stu_rank INT; -- 使用窗口函数计算密集排名 SELECT `rank` INTO stu_rank FROM ( SELECT student_id, DENSE_RANK() OVER (ORDER BY score DESC) AS `rank` FROM scores WHERE course_id = course_id ) AS ranked WHERE student_id = stu_id; RETURN stu_rank; END$$ DELIMITER ; ``` **使用示例**: ```sql SELECT GetCourseRank(2023001, 'CS101'); -- 返回 3 (表示全班第3名) ``` ### 关键实现说明 1. **分数等级判定**:使用CASE语句实现多条件判断,处理边界值时注意: - 当分数在边界值(如80分)时,`>= 80`会正确判定为"良好" - 处理NULL值:无成绩记录时返回'不及格' 2. **排名算法**: - 使用`DENSE_RANK()`确保并列排名后名次连续(如1,2,2,3) - 替代方案`RANK()`会产生不连续名次(1,2,2,4-MySQL 8.0+支持窗口函数 3. **性能优化**: ```sql -- 建议索引 CREATE INDEX idx_scores_stu ON scores(student_id); CREATE INDEX idx_scores_course ON scores(course_id); CREATE INDEX idx_scores_combo ON scores(course_id, score DESC); ``` ### 使用场景示例 ```sql -- 查询学号2023001的成绩情况 SELECT GetGradeLevel(2023001) AS 总评等级, GetCourseAverage('CS101') AS 课程平均分, CheckCourseSelection(2023001, 'CS101') AS 是否选课, GetCourseRank(2023001, 'CS101') AS 课程排名; ``` ### 相关问题 1. MySQL中如何处理分数边界值(如79.5分)的等级判定? 2. 当课程没有学生选修时,`GetCourseAverage()`函数返回什么值? 3. `DENSE_RANK()`和`RANK()`函数在排名计算中有何区别? 4. 如何在MySQL 5.7版本中实现课程排名功能(无窗口函数支持)? 5. 如何优化这些函数在百万级数据量下的查询性能? [^1]: 参考了成绩等级划分的标准实现方式 [^2]: 使用了CASE语句进行多条件判断 [^3]: 采用窗口函数实现排名计算
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值