要查询每门成绩的前十名,可以使用MySQL的存储过程和窗口函数来实现。以下是一个示例存储过程,它使用窗口函数`ROW_NUMBER()`来为每个学生的每门课程分配一个排名,然后选择排名在前十名的记录。
```sql
DELIMITER //
CREATE PROCEDURE GetScRank()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE cno INT;
DECLARE cur CURSOR FOR SELECT DISTINCT cno FROM score;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO cno;
IF done THEN
LEAVE read_loop;
END IF;
WITH RankedScores AS (
SELECT
s.sno,
s.cno,
s.sc,
ROW_NUMBER() OVER (PARTITION BY s.cno ORDER BY s.sc DESC) AS rank
FROM
score s
WHERE
s.cno = cno
)
SELECT
sno,
cno,
sc
FROM
RankedScores
WHERE
rank <= 10
ORDER BY
cno,
rank;
END LOOP;
CLOSE cur;
END //
DELIMITER ;
```
解释
存储过程定义:
`DELIMITER //`:定义存储过程的结束符为`//`。
`CREATE PROCEDURE GetScRank()`:创建一个名为`GetScRank`的存储过程。
变量声明:
`DECLARE done INT DEFAULT FALSE;`:声明一个布尔变量`done`,用于控制游标的结束。
`DECLARE cno INT;`:声明一个整数变量`cno`,用于存储课程编号。
`DECLARE cur CURSOR FOR SELECT DISTINCT cno FROM score;`:声明一个游标`cur`,用于遍历所有不同的课程编号。
`DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;`:声明一个继续处理程序,当游标没有更多记录时,将`done`设置为`TRUE`。
游标循环:
`OPEN cur;`:打开游标`cur`。
`read_loop: LOOP`:开始一个循环,用于遍历游标中的所有课程编号。
`FETCH cur INTO cno;`:从游标中获取下一个课程编号。
`IF done THEN LEAVE read_loop; END IF;`:如果`done`为`TRUE`,则退出循环。
窗口函数应用:
`WITH RankedScores AS (...)`:定义一个公用表表达式(CTE)`RankedScores`,使用`ROW_NUMBER()`窗口函数为每个学生的每门课程分配一个排名。
`SELECT sno, cno, sc FROM RankedScores WHERE rank <= 10`:从`RankedScores`中选择排名在前十名的记录。
结果排序:
`ORDER BY cno, rank`:按课程编号和排名对结果进行排序。
关闭游标:
`CLOSE cur;`:关闭游标`cur`。
结束存储过程定义:
`DELIMITER ;`:恢复默认的结束符`;`。
调用存储过程
要调用这个存储过程,可以使用以下SQL语句:
```sql
CALL GetScRank();
```
这将返回每门课程成绩的前十名记录。