MySQL 经典练习题(第一套)
经典的 MySQL 练习题。
这个练习题感觉挺经典的,我人工筛选了一些我认为有练习价值的,特别简单的没有收录进来。
创建表的代码
需要自己先创建一个数据库。然后再创建表。
1 | CREATE TABLE STUDENT |
练习题
查询Score表中成绩在60到80之间的所有记录
1 | SELECT * |
或
1 | SELECT * |
查询 score 表中成绩为85,86或88的记录
1 | SELECT * |
或
1 | SELECT * |
以 cno 升序、degree降序查询 score 表的所有记录
1 | SELECT * |
注意 ORDER BY
子句中的顺序,交换顺序之后是不一样的。首先根据第一个字段排序,如果第一个字段相同,再根据第二个字段排序。
查询“95031”班的学生人数。
1 | SELECT COUNT(*) |
MySQL 中 COUNT()
函数的使用。
查询Score表中的最高分的学生学号和课程号
1 | SELECT sno, cno |
下面的写法也可以,但是效率不高。
1 | SELECT sno, cno |
查询‘3-105’号课程的平均分。
1 | SELECT AVG(degree) |
MySQL 的 AVG
函数的使用。
查询Score表中至少有5名学生选修的并以3开头的课程的平均分数。
1 | SELECT cno, AVG(degree) |
查询最低分大于70,最高分小于90的Sno列。
本题乍一看不太好理解,可以翻译为:查询所有课程成绩最低分大于70分,最高分小于90分的学生的学号。
题意暗含了要根据 sno
分组的意思。
1 | SELECT sno |
查询所有学生的Sname、Cno和Degree列。
1 | SELECT sname, cno, degree |
查询所有学生的Sno、Cname和Degree列。
1 | SELECT score.sno, cno, degree |
查询所有学生的Sname、Cname和Degree列。
1 | SELECT a.sname, b.cname, c.degree |
查询“95033”班所选课程的平均分。
1 | SELECT AVG(degree) |
查询所有同学的Sno、Cno和rank列。
首先,假设使用如下命令建立了一个grade表:
1 | CREATE TABLE grade ( |
答案:
1 | SELECT a.sno, a.cno, b.rank |
或
1 | SELECT a.Sno, a.Cno, b.rank |
其中使用 BETWEEN ... AND
来进行表的连接。
查询选修“3-105”课程的成绩高于“109”号同学成绩的所有同学的记录。
1 | SELECT * |
查询score中选学一门以上课程的同学中分数为非最高分成绩的记录
这道题目有歧义,请参考本文末尾,我写了一大段用来解释这道题。
查询和学号为108的同学同年出生的所有学生的Sno、Sname和Sbirthday列。
1 | SELECT sno, sname, sbirthday |
涉及到函数 YEAR()
的使用。
查询“张旭“教师任课的学生成绩。
1 | SELECT * |
查询选修某课程的同学人数多于5人的教师姓名。
1 | SELECT tname |
查询95033班和95031班全体学生的记录。
1 | SELECT * |
查询存在有85分以上成绩的课程Cno。
1 | SELECT cno |
查询出“计算机系“教师所教课程的成绩表。
1 | SELECT * |
查询所有教师和同学的name、sex和birthday。
1 | SELECT tname name, tsex sex, tbirthday birthday |
其中使用到了集合运算 UNION
。
查询所有“女”教师和“女”同学的name、sex和birthday。
1 | SELECT tname name, tsex sex, tbirthday birthday |
查询成绩比该课程平均成绩低的同学的成绩表。
1 | SELECT a.* |
查询所有任课教师的Tname和Depart。
1 | SELECT tname, depart |
查询至少有2名男生的班号。
1 | SELECT CLASS |
查询Student表中不姓“王”的同学记录。
1 | SELECT * |
查询Student表中每个学生的姓名和年龄。
1 | SELECT sname, YEAR(NOW()) - YEAR(sbirthday) |
使用到了 NOW()
函数。
对查询score中选学一门以上课程的同学中分数为非最高分成绩的记录的辨析
假设有下面这样一张表,表名为 score
。
sno | cno | degree |
---|---|---|
103 | 3-245 | 86 |
105 | 3-245 | 75 |
109 | 3-245 | 68 |
103 | 3-105 | 92 |
105 | 3-105 | 88 |
109 | 3-105 | 76 |
101 | 3-105 | 64 |
107 | 3-105 | 91 |
101 | 6-166 | 85 |
108 | 3-105 | 79 |
108 | 6-166 | 81 |
考虑下面这个 SQL 语句
1 | SELECT * |
你觉得这句 SQL 语句会返回什么?或许我们是希望返回按照 sno 分组之后的每个组中的所有记录。
实际上,这个语句只会返回每个分组中排名最靠前的一条记录(有可能是按照原表中数据的顺序排列的)。
假设我们想要查询 score 中选学一门以上课程的同学中分数为非最高分成绩的记录。
我们可以能会写出这样的 SQL 语句:
1 | SELECT * |
HAVING degree != MAX(degree)
会对每个分组中的每一条记录进行判断吗?
实际上并不会,只会对每个分组中排名最靠前的一条记录进行判断。所以这个 SQL 并不会正确地返回期望的答案。其实 SQL 标准是不允许有这种存在含混不清的语句存在的,可以参考关于 MySQL 的 ONLY_FULL_GROUP_BY 的相关内容。
因为本题的题目的表述有歧义,可能有两种解读:
其一种是查询 score 中选学一门以上课程的同学中,分数不是其自己所有课程成绩的最高分的记录。
另一种是查询 score 中选学一门以上课程的同学中,其课程分数不是该课程所有学生成绩最高分的记录。
针对第一种理解方式的答案:
1 | SELECT a.* |
这个写法没有显式地体现出“选学一门以上课程”这个条件,是因为如果一个人只选了一门课程,那么这门课程的成绩在子查询中一定是 max_degree
,所以在外层查询一定会被过滤掉。
针对第二种理解方式的答案:
1 | SELECT a.* |
针对于第一种理解方式,这还有一个错误的答案,供参考,用来防止踩坑:
1 | -- 这个不行,因为一个学生的不是最大成绩的记录, |
题目来源
经过一番搜索,发现这套题出自《SQL Server 2000应用系统开发教程》。
豆瓣链接:SQL Server 2000应用系统开发教程 - 2005版
上边那个是2005版的,没有图书封面图片。还有一个2008版的链接,有图书封面图片:SQL Server 2000应用系统开发教程 - 2008版
Google Books 可以查看这本书的电子版的部分章节:SQL Server 2000应用系统开发教程 - Google Books
如何找到的(用了什么搜索技巧)
使用谷歌搜索 曾华 匡明 王丽 教材
,就直接找到 Google Books 提供的信息了。