从网上寻的一套SQL经典50题,平时常用mysql,便自己针对mysql版本也把这作业补上了。放上来只当是交流,若发现不妥当的可以留言指导~
思路
- 确定结果包含内容(包含几个表、是否都是表的字段):
- 是否排序;
- 是否分组聚合;
- 是否使用函数(函数的坑稍微留意);
- 表连接和子查询哪个更直接
- 性能问题,多种写法对比
涉及点
- 简单查询
- 表连接查询
- 子查询
- 分组查询
- 组内排序
- 函数使用
- 变量使用
题目难度并不是逐一递增,总体会有以上几个涉及点,可以通过题目关键字搜索自己感兴趣的。
练习数据库准备
-- 学生表
CREATE TABLE `Student`(
`s_id` VARCHAR(20),
`s_name` VARCHAR(20) NOT NULL DEFAULT '',
`s_birth` VARCHAR(20) NOT NULL DEFAULT '',
`s_sex` VARCHAR(10) NOT NULL DEFAULT '',
PRIMARY KEY(`s_id`)
);
-- 课程表
CREATE TABLE `Course`(
`c_id` VARCHAR(20),
`c_name` VARCHAR(20) NOT NULL DEFAULT '',
`t_id` VARCHAR(20) NOT NULL,
PRIMARY KEY(`c_id`)
);
-- 教师表
CREATE TABLE `Teacher`(
`t_id` VARCHAR(20),
`t_name` VARCHAR(20) NOT NULL DEFAULT '',
PRIMARY KEY(`t_id`)
);
-- 成绩表
CREATE TABLE `Score`(
`s_id` VARCHAR(20),
`c_id` VARCHAR(20),
`s_score` INT(3),
PRIMARY KEY(`s_id`,`c_id`)
);
-- 插入学生表测试数据
insert into Student values('01' , '赵雷' , '1990-01-01' , '男');
insert into Student values('02' , '钱电' , '1990-12-21' , '男');
insert into Student values('03' , '孙风' , '1990-05-20' , '男');
insert into Student values('04' , '李云' , '1990-08-06' , '男');
insert into Student values('05' , '周梅' , '1991-12-01' , '女');
insert into Student values('06' , '吴兰' , '1992-03-01' , '女');
insert into Student values('07' , '郑竹' , '1989-07-01' , '女');
insert into Student values('08' , '王菊' , '1990-01-20' , '女');
-- 课程表测试数据
insert into Course values('01' , '语文' , '02');
insert into Course values('02' , '数学' , '01');
insert into Course values('03' , '英语' , '03');
-- 教师表测试数据
insert into Teacher values('01' , '张三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');
-- 成绩表测试数据
insert into Score values('01' , '01' , 80);
insert into Score values('01' , '02' , 90);
insert into Score values('01' , '03' , 99);
insert into Score values('02' , '01' , 70);
insert into Score values('02' , '02' , 60);
insert into Score values('02' , '03' , 80);
insert into Score values('03' , '01' , 80);
insert into Score values('03' , '02' , 80);
insert into Score values('03' , '03' , 80);
insert into Score values('04' , '01' , 50);
insert into Score values('04' , '02' , 30);
insert into Score values('04' , '03' , 20);
insert into Score values('05' , '01' , 76);
insert into Score values('05' , '02' , 87);
insert into Score values('06' , '01' , 31);
insert into Score values('06' , '03' , 34);
insert into Score values('07' , '02' , 89);
insert into Score values('07' , '03' , 98);
题目及答案
-- 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数
SELECT st.*,
s1.`s_score`,
s2.`s_score`
FROM `Student` st
LEFT JOIN Score s1 ON s1.`s_id` = st.`s_id` AND s1.`c_id` = '01'
LEFT JOIN Score s2 ON s2.`s_id` = st.`s_id` AND s2.`c_id` = '02'
WHERE s1.`s_score` > s2.`s_score`;
-- 2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数
SELECT st.*,
s1.`s_score`,
s2.`s_score`
FROM `Student` st
LEFT JOIN Score s1 ON s1.`s_id` = st.`s_id` AND s1.`c_id` = '01'
LEFT JOIN Score s2 ON s2.`s_id` = st.`s_id` AND s2.`c_id` = '02'
WHERE s1.`s_score` < s2.`s_score` ;
-- 3、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩
SELECT st.*,
avg(s.`s_score`)
FROM `Student` st ,
Score s
WHERE st.`s_id` = s.s_id
GROUP BY st.s_id HAVING avg(s.`s_score`) >= 60;
-- 4、查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩
SELECT st.*,
avg(s.`s_score`) score
FROM `Student` st ,
Score s
WHERE st.`s_id` = s.s_id
GROUP BY st.s_id HAVING avg(s.`s_score`) < 60;
UNION
SELECT st.*,
'-'
FROM `Student` st
LEFT JOIN Score s ON s.`s_id` = st.`s_id`
WHERE s.`s_id` IS NULL;
-- 5、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩
SELECT s.*,
count(sc.`c_id`),
sum(sc.`s_score`)
FROM Student s ,
Score sc
WHERE s.`s_id` = sc.`s_id`
GROUP BY s.`s_id`;
-- 6、查询"李"姓老师的数量
SELECT count(*)
FROM `Teacher` t
WHERE t.`t_name` LIKE '李%';
-- 7、查询学过"张三"老师授课的同学的信息
SELECT st.*
FROM `Student` st,
Score s,
Course c,
`Teacher` t
WHERE st.`s_id` = s.`s_id`
AND c.`t_id` = t.`t_id`
AND s.`c_id` = c.`c_id`
AND t.`t_name` = '张三';
-- 8、查询没学过"张三"老师授课的同学的信息
SELECT *
FROM Student ss
WHERE ss.`s_id` NOT IN
(SELECT st.`s_id`
FROM `Student` st,
Score s,
Course c,
`Teacher` t
WHERE st.`s_id` = s.`s_id`
AND c.`t_id` = t.`t_id`
AND s.`c_id` = c.`c_id`
AND t.`t_name` = '张三');
-- 9、查询学过编号为"01"并且也学过编号为"02"的课程的同学的信息
SELECT *
FROM Student st
WHERE EXISTS
(SELECT 1
FROM Score s
WHERE s.`s_id` = st.`s_id`
AND s.`c_id` = '01')
AND EXISTS
(SELECT 1
FROM Score s
WHERE s.`s_id` = st.`s_id`
AND s.`c_id` = '02');
-- 10、查询学过编号为"01"但是没有学过编号为"02"的课程的同学的信息
SELECT *
FROM Student st
LEFT JOIN Score s1 ON s1.`s_id` = st.`s_id` AND s1.`c_id` = '01'
LEFT JOIN Score s2 ON s2.`s_id` = st.s_id AND s2.`c_id` = '02'
WHERE s1.`s_id` IS NOT NULL
AND s2.`s_id` IS NULL;
-- 11、查询没有学全所有课程的同学的信息
SELECT st.*
FROM Student st ,
Score s
WHERE st.s_id = s.s_id
GROUP BY st.s_id HAVING COUNT(s.c_id) <
(SELECT COUNT(*)
FROM Course);
-- 12、查询至少有一门课与学号为"01"的同学所学相同的同学的信息
SELECT DISTINCT st.*
FROM Student st,
Score s
WHERE st.s_id = s.s_id
AND st.s_id <> '01'
AND s.c_id IN
(SELECT ss.c_id
FROM Score ss
WHERE ss.s_id = '01');
-- 13、查询和"01"号的同学学习的课程完全相同的其他同学的信息
SELECT *
FROM Student st
WHERE st.s_id IN
(SELECT ss.s_id
FROM Score ss
WHERE ss.c_id IN
(SELECT s.c_id
FROM Score s
WHERE s.s_id = '01')
GROUP BY ss.s_id HAVING COUNT(*) =
(SELECT COUNT(*)
FROM Score sss
WHERE sss.s_id = '01'));
-- 14、查询没学过"张三"老师讲授的任一门课程的学生姓名
SELECT *
FROM Student st
WHERE NOT EXISTS
(SELECT 1
FROM Score s
LEFT JOIN Course c ON s.`c_id` = c.`c_id`
LEFT JOIN Teacher t ON t.`t_id` = c.`t_id`
WHERE t.`t_name` = '张三'
AND s.`s_id` = st.`s_id`);
-- 15、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
SELECT st.*,
temp.*
FROM Student st
INNER JOIN
(SELECT s_id,
COUNT(*) COUNT, AVG(s_score)
FROM Score s
WHERE s.s_score < 60
GROUP BY s.s_id HAVING COUNT(*) >= 2) temp ON st.s_id = temp.s_id;
-- 16、检索"01"课程分数小于60,按分数降序排列的学生信息
SELECT * FROM Student st
LEFT JOIN Score s ON s.s_id = st.s_id
WHERE s.c_id = '01'
AND s.s_score < 60
ORDER BY s.s_score;
-- 17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
SELECT *
FROM Student st
LEFT JOIN
(SELECT s.s_id ,
AVG(s.s_score) avgScore
FROM Score s
GROUP BY s.s_id) TEMP ON TEMP.s_id = st.s_id
ORDER BY TEMP.avgScore;
SELECT st.* ,
s1.s_score ,
s2.s_score ,
s3.s_score ,
(SELECT avg(s.s_score) FROM Score s WHERE s.s_id = st.s_id) avgScore
FROM Student st
LEFT JOIN Score s1 ON s1.s_id = st.s_id AND s1.c_id = '01'
LEFT JOIN Score s2 ON s2.s_id = st.s_id AND s2.c_id = '02'
LEFT JOIN Score s3 ON s3.s_id = st.s_id AND s3.c_id = '03'
ORDER BY avgScore;
-- 18.查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
--及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90
SELECT c.c_id,
c.c_name,
MAX(s_score) MAX,
MIN(s_score) MIN,
ROUND(AVG(s_score), 2) AVG,
ROUND(SUM(CASE WHEN s_score >= 60 THEN 1 ELSE 0 END)/COUNT(CASE WHEN s_score THEN 1 ELSE 0 END), 2) '及格',
ROUND(SUM(CASE WHEN s_score >= 70 AND s_score < 80 THEN 1 ELSE 0 END)/COUNT(CASE WHEN s_score THEN 1 ELSE 0 END), 2) '中等',
ROUND(SUM(CASE WHEN s_score >= 80 AND s_score < 90 THEN 1 ELSE 0 END)/COUNT(CASE WHEN s_score THEN 1 ELSE 0 END), 2) '优良',
ROUND(SUM(CASE WHEN s_score >= 90 THEN 1 ELSE 0 END)/COUNT(CASE WHEN s_score THEN 1 ELSE 0 END), 2) '优秀'
FROM Course c
LEFT JOIN Score s ON s.c_id = c.c_id
GROUP BY c.c_id;
-- 19、按各科成绩进行排序,并显示排名
SELECT
a.s_id,
a.c_id,
@i:=@i +1 AS i保留排名,
@k:=(CASE WHEN @score=a.s_score THEN @k ELSE @i END) AS rank不保留排名,
@score:=a.s_score AS score
FROM
(SELECT s_id,
c_id,
s_score
FROM Score
GROUP BY s_id,
c_id,
s_score
ORDER BY s_score DESC)a,
(SELECT @k:=0,@i:=0,@score:=0)s
另一种写法:
SELECT
s.c_id,
s.s_id,
s.s_score,
COUNT(s.s_score < s2.s_score) + 1 rank
FROM Score s
LEFT JOIN Score s2 ON s2.c_id = s.c_id AND s.s_score < s2.s_score
GROUP BY s.c_id , s.s_id
ORDER BY s.c_id, s.s_score desc;
-- 20、查询学生的总成绩并进行排名
SELECT *
FROM Student st
LEFT JOIN (
SELECT s.s_id, SUM(s.s_score) AS sum
FROM Score s
GROUP BY s.s_id
) temp
ON temp.s_id = st.s_id
ORDER BY temp.sum DESC;
-- 21、查询不同老师所教不同课程平均分从高到低显示
SELECT c.c_name, t.t_name, s.c_id
, ROUND(AVG(s.s_score), 2) AS avgScore
FROM Score s
LEFT JOIN Course c ON c.c_id = s.c_id
LEFT JOIN Teacher t ON t.t_id = c.t_id
GROUP BY s.c_id
ORDER BY avgScore DESC;
-- 22、查询所有课程的成绩第2名到第3名的学生信息及该课程成绩
SELECT *
FROM Student st
INNER JOIN
(SELECT *
FROM
(SELECT s.*,
COUNT(s.s_score < s1.s_score) + 1 rankNo
FROM Score s
LEFT JOIN Score s1 ON s.c_id = s1.c_id
AND s.s_score < s1.s_score
GROUP BY s.c_id,
s.s_id
ORDER BY s.c_id,
s.s_score DESC) TEMP
WHERE rankNo > 1
AND rankNo < 4) temp2 ON temp2.s_id = st.s_id;
-- 23、统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[0-60]及所占百分比
SELECT temp.*
, round(temp.`0-60` / COUNT(ss.s_id), 2) AS '0-60%'
, round(temp.`0-60` / COUNT(ss.s_id), 2) AS '60-70%'
, round(temp.`0-60` / COUNT(ss.s_id), 2) AS '70-85%'
, round(temp.`0-60` / COUNT(ss.s_id), 2) AS '0-60%'
FROM (
SELECT s.c_id, SUM(CASE
WHEN s.s_score < 60 THEN 1
ELSE 0
END) AS '0-60', SUM(CASE
WHEN s.s_score >= 60
AND s.s_score < 70 THEN 1
ELSE 0
END) AS '60-70'
, SUM(CASE
WHEN s.s_score >= 70
AND s.s_score < 85 THEN 1
ELSE 0
END) AS '70-85', SUM(CASE
WHEN s.s_score >= 85 THEN 1
ELSE 0
END) AS '85-100'
FROM Score s
GROUP BY s.c_id
) TEMP
LEFT JOIN Score ss ON ss.c_id = TEMP.c_id
GROUP BY ss.c_id;
-- 24、查询学生平均成绩及其名次
SELECT result.*, @rankNo := @rankNo + 1
FROM (
SELECT s.s_id, round(AVG(s.`s_score`), 2) AS avgScore
FROM Score s
GROUP BY s.`s_id`
ORDER BY avgScore DESC
) result, (
SELECT @rankNo := 0
) temp;
-- 25、查询各科成绩前三名的记录
SELECT s.`c_id`, s.`s_id`, s.`s_score`
, COUNT(s.`s_score` < s2.`s_score`) + 1 AS rankNo
FROM Score s
LEFT JOIN Score s2
ON s2.`c_id` = s.`c_id`
AND s2.`s_score` > s.`s_score`
GROUP BY s.`c_id`, s.`s_id`
HAVING COUNT(s.`s_score` < s2.`s_score`) < 3
ORDER BY s.`c_id`, s.`s_score` DESC;
-- 26、查询每门课程被选修的学生数
SELECT s.`c_id`,
count(*)
FROM Score s
GROUP BY s.`c_id`;
-- 27、查询出只有两门课程的全部学生的学号和姓名
SELECT st.*
FROM Student st
WHERE st.`s_id` IN (
SELECT s.s_id
FROM Score s
GROUP BY s.`s_id`
HAVING COUNT(s.`c_id`) = 2
);
-- 28、查询男生、女生人数
SELECT st.`s_sex`, COUNT(st.`s_sex`)
FROM Student st
GROUP BY st.`s_sex`;
-- 29、查询名字中含有"风"字的学生信息
SELECT *
FROM Student st
WHERE st.`s_name` LIKE '%风%';
-- 30、查询同名学生名单,并统计同名人数
SELECT *
FROM Student st, (
SELECT st.`s_name`, COUNT(*)
FROM Student st
GROUP BY st.`s_name`
) temp
WHERE st.s_name = temp.s_name;
-- 31、查询1990年出生的学生名单
SELECT *
FROM Student st
WHERE st.`s_birth` BETWEEN '1990-01-01' AND '1990-12-31';
-- 32、查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
SELECT *
FROM Course c, (
SELECT s.`c_id`, round(AVG(s.`s_score`), 2) AS avgScore
FROM Score s
GROUP BY s.`c_id`
) temp
WHERE c.c_id = temp.`c_id`
ORDER BY avgScore DESC, c.c_id;
-- 33、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩
SELECT *
FROM Student st
INNER JOIN (
SELECT s.s_id, round(AVG(s.`s_score`), 2) AS avgScore
FROM Score s
GROUP BY s.`s_id`
HAVING round(AVG(s.`s_score`), 2) >= 85
) temp
ON temp.s_id = st.`s_id`;
-- 34、查询课程名称为"数学",且分数低于60的学生姓名和分数
SELECT st.*, s.`s_score`
FROM Score s
LEFT JOIN Course c ON c.`c_id` = s.`c_id`
LEFT JOIN Student st ON st.`s_id` = s.`s_id`
WHERE c.`c_name` = '数学'
AND s.`s_score` < 60;
-- 35、查询所有学生的课程及分数情况
SELECT st.*,
s1.`s_score` AS '语',
s2.`s_score` AS '数',
s3.`s_score` AS '英'
FROM Student st
LEFT JOIN Score s1 ON s1.`s_id` = st.`s_id` AND s1.`c_id` = '01'
LEFT JOIN Score s2 ON s2.`s_id` = st.`s_id` AND s2.`c_id` = '02'
LEFT JOIN Score s3 ON s3.`s_id` = st.`s_id` AND s3.`c_id` = '03';
-- 36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数
SELECT st.*,
c.`c_name`,
s.`s_score`
FROM Score s,
Student st,
Course c
WHERE s.`s_id` = st.`s_id`
AND s.`c_id` = c.`c_id`
AND s.`s_score` >= 70;
-- 37、查询不及格的课程
SELECT c.*,
min(s.`s_score`) minScore
FROM Score s,
`Course` c
WHERE s.`c_id` = c.`c_id`
AND s.`s_score` < 60
GROUP BY c.`c_id`;
-- 38、查询课程编号为01且课程成绩在80分以上的学生的学号和姓名
SELECT *
FROM Student st ,
Score s
WHERE st.`s_id` = s.`s_id`
AND s.`c_id` = '01'
AND s.`s_score` >= 80;
-- 39、求每门课程的学生人数
SELECT c.`c_name`,
count(s.`s_id`) studentCount
FROM Score s,
Course c
WHERE s.c_id = c.c_id
GROUP BY s.`c_id`;
-- 40、查询选修"张三"老师所授课程的学生中,成绩最高的学生信息及其成绩
SELECT *
FROM Student st
INNER JOIN
(SELECT s.`s_id`,
s.`s_score`
FROM Score s
WHERE EXISTS
(SELECT 1
FROM Course c,
Teacher t
WHERE c.`t_id` = t.`t_id`
AND t.`t_name` = '张三'
AND s.`c_id` = c.`c_id`)
ORDER BY s.`s_score` DESC LIMIT 1) TEMP ON st.`s_id` = TEMP.`s_id`;
-- 41、查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩
SELECT DISTINCT s1.`s_id`,
s1.`c_id`,
s1.`s_score`
FROM Score s1
INNER JOIN Score s2 ON s2.`s_score` = s1.`s_score`
AND s1.`c_id` <> s2.`c_id`;
-- 42、查询每门功成绩最好的前两名
SELECT s1.`c_id`,
s1.`s_id`,
s1.`s_score`
FROM Score s1
LEFT JOIN Score s2 ON s2.`s_score` > s1.`s_score`
AND s1.`c_id` = s2.`c_id`
GROUP BY s1.`c_id`,
s1.`s_id` HAVING count(s1.`s_score` < s2.`s_score`) <= 1
ORDER BY s1.`c_id`,
s1.`s_score` DESC;
另一种写法:
SELECT a.s_id,
a.c_id,
a.s_score
FROM score a
WHERE
(SELECT COUNT(1)
FROM score b
WHERE b.c_id=a.c_id
AND b.s_score>=a.s_score)<=2
ORDER BY a.c_id;
-- 43、统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
SELECT s.`c_id`,
count(*) sCount
FROM Score s
GROUP BY s.`c_id` HAVING count(*) >= 5
ORDER BY sCount DESC,
s.`c_id`;
-- 44、检索至少选修两门课程的学生学号
SELECT s.`s_id`
FROM Score s
GROUP BY s.`s_id` HAVING count(*) >= 2;
-- 45、查询选修了全部课程的学生信息
SELECT s.`s_id`
FROM Score s
GROUP BY s.`s_id` HAVING count(*) =
(SELECT count(*)
FROM Course);
-- 46、查询各学生的年龄
SELECT st.*
, DATE_FORMAT(Now(), '%Y') - DATE_FORMAT(st.`s_birth`, '%Y') - CASE
WHEN DATE_FORMAT(now(), '%m-%d') < DATE_FORMAT(st.`s_birth`, '%m-%d') THEN 1
ELSE 0
END AS age
FROM Student st;
-- 47、查询本周过生日的学生
SELECT st.*,
WEEK(st.`s_birth`)
FROM Student st
WHERE WEEK(NOW()) = WEEK(st.`s_birth`);
(不同年度的日期对应的周值不一定相同!)
SELECT st.*,
WEEK(st.`s_birth`),
WEEK(concat(YEAR(now()),'-',right(st.`s_birth`,5)))
FROM Student st
WHERE WEEK(NOW()) = WEEK(concat(YEAR(now()),'-',right(st.`s_birth`,5)));
-- 48、查询下周过生日的学生
SELECT st.*,
WEEK(st.`s_birth`),
WEEK(concat(YEAR(now()),'-',right(st.`s_birth`,5)))
FROM Student st
WHERE WEEK(NOW()) = WEEK(concat(YEAR(now()),'-',right(st.`s_birth`,5))) - 1;
-- 49、查询本月过生日的学生
SELECT st.*,
DATE_FORMAT(st.`s_birth`, '%m')
FROM Student st
WHERE DATE_FORMAT(NOW(), '%m') = DATE_FORMAT(st.`s_birth`, '%m');
-- 50、查询下月过生日的学生
SELECT st.*,
DATE_FORMAT(st.`s_birth`, '%m')
FROM Student st
WHERE DATE_FORMAT(NOW(), '%m') = DATE_FORMAT(st.`s_birth`, '%m') -1;
部分有参考了启明星的指引
童鞋的博文~