title: MySQL 数据库SQL练习
tags: MySQL,练习
grammar_cjkRuby: true
网上很多类似的SQL运行总是出现这样的那样的问题,或者是写的异常复杂,本着研究练习的心态写下此教程。注:本教程sql
全部在MySQL
数据库上经过测试,如发现问题,请下方留言,转载请注明出处,谢谢!
数据库参数导入
/*
Navicat MySQL Data Transfer
Source Server : mysql
Source Server Version : 50528
Source Host : localhost:3306
Source Database : student_course_test
Target Server Type : MYSQL
Target Server Version : 50528
File Encoding : 65001
Date: 2016-06-19 16:34:13
CREATE BY MARVIS.ZHAO
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `course`
-- ----------------------------
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`C` varchar(10) NOT NULL DEFAULT '',
`Cname` varchar(10) DEFAULT NULL,
`T` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES ('01', '语文', '02');
INSERT INTO `course` VALUES ('02', '数学', '01');
INSERT INTO `course` VALUES ('03', '英语', '03');
-- ----------------------------
-- Table structure for `sc`
-- ----------------------------
DROP TABLE IF EXISTS `sc`;
CREATE TABLE `sc` (
`S` varchar(10) DEFAULT NULL,
`C` varchar(10) DEFAULT NULL,
`score` decimal(18,1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sc
-- ----------------------------
INSERT INTO `sc` VALUES ('01', '01', '80.0');
INSERT INTO `sc` VALUES ('01', '02', '90.0');
INSERT INTO `sc` VALUES ('01', '03', '99.0');
INSERT INTO `sc` VALUES ('02', '01', '70.0');
INSERT INTO `sc` VALUES ('02', '02', '60.0');
INSERT INTO `sc` VALUES ('02', '03', '80.0');
INSERT INTO `sc` VALUES ('03', '01', '80.0');
INSERT INTO `sc` VALUES ('03', '02', '80.0');
INSERT INTO `sc` VALUES ('03', '03', '80.0');
INSERT INTO `sc` VALUES ('04', '01', '50.0');
INSERT INTO `sc` VALUES ('04', '02', '30.0');
INSERT INTO `sc` VALUES ('04', '03', '20.0');
INSERT INTO `sc` VALUES ('05', '01', '76.0');
INSERT INTO `sc` VALUES ('05', '02', '87.0');
INSERT INTO `sc` VALUES ('06', '01', '31.0');
INSERT INTO `sc` VALUES ('06', '03', '34.0');
INSERT INTO `sc` VALUES ('07', '02', '89.0');
INSERT INTO `sc` VALUES ('07', '03', '98.0');
INSERT INTO `sc` VALUES ('09', '01', '30.0');
-- ----------------------------
-- Table structure for `student`
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`S` varchar(10) DEFAULT NULL,
`Sname` varchar(10) DEFAULT NULL,
`Sage` datetime DEFAULT NULL,
`Ssex` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('01', '赵小六', '1990-09-01 03:18:03', '男');
INSERT INTO `student` VALUES ('02', '钱钟书', '1916-12-21 00:00:00', '男');
INSERT INTO `student` VALUES ('03', '孙菲菲', '1990-05-20 00:00:00', '男');
INSERT INTO `student` VALUES ('04', '李东升', '1990-08-06 00:00:00', '男');
INSERT INTO `student` VALUES ('05', '周佛海', '1900-12-01 00:00:00', '女');
INSERT INTO `student` VALUES ('06', '吴天', '1992-03-01 00:00:00', '女');
INSERT INTO `student` VALUES ('07', '郑板桥', '1989-07-01 00:00:00', '女');
INSERT INTO `student` VALUES ('08', '王菊', '1990-01-20 00:00:00', '女');
INSERT INTO `student` VALUES ('09', '张凯', '1983-08-01 20:10:21', '男');
INSERT INTO `student` VALUES ('10', '李云', '1989-05-19 11:07:30', '男');
INSERT INTO `student` VALUES ('11', '张凯', '1997-06-12 11:47:23', '男');
INSERT INTO `student` VALUES ('12', '张凯', '1997-06-16 11:47:23', '女');
INSERT INTO `student` VALUES ('13', '张海洋', '1989-05-14 11:07:30', '男');
-- ----------------------------
-- Table structure for `teacher`
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`T` varchar(10) DEFAULT NULL,
`Tname` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('01', '张三');
INSERT INTO `teacher` VALUES ('02', '李魁');
INSERT INTO `teacher` VALUES ('03', '王一凡');
INSERT INTO `teacher` VALUES ('04', '赵廓');
数据库练习
1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数
SELECT * FROM student s
WHERE EXISTS
(SELECT 1 from sc a
JOIN sc b
ON a.S = b.S
WHERE a.C = '01' AND b.C='02' AND a.score > b.score AND s.S=a.S)
2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数
SELECT s.*
,c.score
FROM student s
,
(SELECT a.S as s_id
,a.Score as score
from sc a
JOIN sc b
ON a.S = b.S
WHERE a.C = '01' AND b.C='02' AND a.score < b.score) c
WHERE s.S = c.s_id
3、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩
SELECT s.S
,s.Sname
,c.avg
FROM student as s
JOIN
(SELECT sc.S as s_id
,AVG(score) as avg
FROM sc
GROUP BY sc.S
HAVING AVG(score) > 60) as c
ON c.s_id = s.S
4、查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩
SELECT s.S
,s.Sname
,c.avg
FROM student as s
JOIN
(SELECT sc.S as s_id
,AVG(score) as avg
FROM sc
GROUP BY sc.S
HAVING AVG(score) < 60) as c
ON c.s_id = s.S
5、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩
SELECT s.S
,s.Sname
,c.cnt
,c.total
FROM student s
LEFT OUTER JOIN
(SELECT sc.S as s_id
,COUNT(sc.C) as cnt
,SUM(sc.score) as total
FROM sc
GROUP BY sc.S
)c
ON s.S = c.s_id
6、查询"李"姓老师的数量
SELECT COUNT(1)
FROM teacher
WHERE Tname LIKE '李%'
7、查询学过"张三"老师授课的同学的信息
SELECT *
FROM student s
WHERE EXISTS (
SELECT DISTINCT sc.S
FROM sc
WHERE EXISTS
(
SELECT c.C
FROM teacher t
JOIN course c
ON c.T = t.T
WHERE t.Tname='张三' AND sc.C = c.C)
AND s.S=sc.S
)
8、查询没学过"张三"老师授课的同学的信息
SELECT *
FROM student s
WHERE NOT EXISTS (
SELECT DISTINCT sc.S
FROM sc
WHERE EXISTS
(
SELECT c.C
FROM teacher t
JOIN course c
ON c.T = t.T
WHERE t.Tname='张三' AND sc.C = c.C)
AND s.S=sc.S
)
9、查询学过编号为"01"并且也学过编号为"02"的课程的同学的信息
SELECT * FROM student s
WHERE EXISTS(
SELECT 1
FROM sc s1
JOIN sc s2
ON s1.S=s2.S
WHERE s1.C='01' AND s2.C='02' AND s.S=s1.S
)
10、查询学过编号为"01"但是没有学过编号为"02"的课程的同学的信息 有问题
1 查出学了01课程的学生
2 查出没有学过02课程的学生
3 组合关联一下查出1,2的综合
第一种写法
select *
from student c
join sc a
on c.S=a.S and a.C='01'
where not exists
(select 1 from sc b where a.S=b.S and b.C='02')
第二种写法
select *
from(
SELECT *
from student s
where exists(select 1 from sc sc where sc.c='01' and sc.s=s.s)
) a
inner join(
SELECT *
from student s
where not exists(select 1 from sc sc where sc.c='02' and sc.s=s.s)
)b
on a.s=b.s
11、查询没有学全所有课程的同学的信息
1 查出所有的课程数
2 按学生分组查找C个数小于上一步的个数的学生
3 匹配学生表找出学生信息
SELECT *
FROM student s
JOIN (SELECT S
FROM sc
JOIN (SELECT COUNT(1) as cnt
FROM course)c
GROUP BY S
HAVING COUNT(C) < max(c.cnt))b
ON s.S = b.S
12、查询至少有一门课与学号为"01"的同学所学相同的同学的信息
1找出01学生所学的课程
2 查找学过这些课程的学生
3 排除01
SELECT *
FROM student s
JOIN sc
ON s.S=sc.S AND sc.C IN
(SELECT DISTINCT C
FROM sc
WHERE sc.S='01')
WHERE s.S!='01'
GROUP BY s.S
13、查询和"01"号的同学学习的课程完全相同的其他同学的信息
1 选出01号同学所学课程
2 选出其他同学的课程
该方法只是查出了与01学生所学课程数目完全相同的学生信心
select * from student where S in (
SELECT DISTINCT S
FROM(
select S
from sc
where C in (select C from sc where S = '01')
and S != '01' -- 这个,过滤掉自己
group by S
having count(*) = (select count(*) from sc where S = '01')
)a
);
该方法貌似有问题,是上面的改版
SELECT * FROM (
select S,
c
from sc
where C in (select C from sc where S = '01')
and S != '01'
GROUP BY s HAVING COUNT(*) = (select count(*) from sc where S = '01')
)t1
WHERE t1.c NOT IN(
SELECT distinct c FROM sc WHERE c
not IN (select distinct C from sc where S = '01')
)
该方法比较简单便于使用
SELECT *
FROM student s
JOIN
(
SELECT s1.s AS s1
,s2.s AS s2
,s1.c AS c1
,s2.c AS c2
FROM
(
SELECT s
,c
FROM sc
WHERE s='01'
) s1
JOIN sc s2
ON s1.s!=s2.s
WHERE s1.c=s2.c
GROUP BY s2.s
HAVING COUNT(*) = (select count(*) from sc where S = '01')
)b
ON s.S = b.s2
14、查询没学过"张三"老师讲授的任一门课程的学生姓名
SELECT *
FROM student
WHERE S NOT IN (
select S
from sc
where C in (SELECT C
FROM course c
JOIN teacher t
ON c.T = t.T
WHERE t.Tname='张三')
group by S
)
15、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
1 选出所有不及格的记录
2 根据S group by 查找不及格的同学,及其平均分
3 根据筛选出的S查出学生信息展示
SELECT *
FROM student
WHERE S IN(
SELECT S
FROM sc
WHERE score < 60
GROUP BY S
HAVING COUNT(*)>=2
)
16、检索"01"课程分数小于60,按分数降序排列的学生信息
SELECT s.*
,sc.score
FROM student s
JOIN sc
ON sc.S = s.S
WHERE sc.score <60 AND sc.C = '01'
ORDER BY sc.score DESC
17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
SELECT s.Sname
,b.C
,b.score
,b.avg
FROM student s
LEFT JOIN
(
SELECT s1.S
,s1.C
,s1.score
,s2.avg
FROM sc s1
JOIN (
SELECT S
,AVG(sc.score) AS avg
FROM sc
GROUP BY S)s2
ON s1.S = s2.S
)b
ON s.S = b.S
ORDER BY b.avg DESC
18、查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90
SELECT c.C
,c.Cname
,a.top
,a.bott
,a.avg
,ROUND((a.lev1/a.cnt),2) AS good
,ROUND((a.lev2/a.cnt),2) AS great
,ROUND((a.lev3/a.cnt),2) AS excellent
,ROUND((a.lev4/a.cnt),2) AS best
FROM course c
JOIN
(SELECT C
,SUM(CASE WHEN score >= 60 THEN 1 ELSE 0 END) lev1
,SUM(CASE WHEN score >= 70 AND score < 80 THEN 1 ELSE 0 END) lev2
,SUM(CASE WHEN score >= 80 AND score < 90 THEN 1 ELSE 0 END) lev3
,SUM(CASE WHEN score >= 90 THEN 1 ELSE 0 END) lev4
,MAX(score) AS top
,MIN(score) AS bott
,AVG(score) AS avg
,COUNT(*) AS cnt
FROM sc
GROUP BY C
)a
ON c.C=a.C
19、按各科成绩进行排序,并显示排名 有问题
-- 第一种全排序
set @x=0;
SELECT sc.C
,sc.S
,sc.score
,@x:=ifnull(@x,0)+1 as rownum
FROM sc
ORDER BY C, sc.score DESC;
第二种按照科目成绩进行排序
set @px=0;
SELECT a.c
,a.s
,a.score
,@px:=(SELECT COUNT(*) FROM sc WHERE c=a.c AND score>a.score)+1 AS ran
FROM sc a
ORDER BY a.C,a.score DESC
20、查询学生的总成绩并进行排名
set @x=0;
SELECT st.*
,a.total
,a.rownum
FROM student st
JOIN
(SELECT s
,SUM(score) as total
,@x:=ifnull(@x,0)+1 as rownum
from sc
GROUP BY s)a
ON st.s=a.s
21、查询不同老师所教不同课程平均分从高到低显示
SELECT t.Tname
,c.Cname
,a.avg
FROM teacher t
JOIN course c
ON t.T=c.T
JOIN (
SELECT c
,AVG(score) as avg
FROM sc
GROUP BY c)a
ON a.c=c.C
ORDER BY a.avg DESC
22、查询所有课程的成绩第2名到第3名的学生信息及该课程成绩
set @px=0;
SELECT s.Sname
,c.Cname
,b.score
,b.ran
FROM student s
JOIN(
SELECT a.c
,a.s
,a.score
,@px:=(SELECT COUNT(*) FROM sc WHERE c=a.c AND score>a.score)+1 AS ran
FROM sc a
-- ORDER BY a.C,a.score DESC -- 这句没啥用了
)b
ON s.s = b.s
JOIN course c
ON c.c=b.c
WHERE b.ran BETWEEN 2 AND 3
ORDER BY c.Cname
23、统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[0-60]及所占百分比
SELECT c.c
,c.Cname
,ROUND(lev1/cnt,2) best
,ROUND(lev2/cnt,2) better
,ROUND(lev3/cnt,2) good
,ROUND(lev4/cnt,2) not_well
FROM course c
JOIN (
SELECT c
,SUM(CASE WHEN score>=85 AND score <= 100 THEN 1 ELSE 0 END) AS lev1
,SUM(CASE WHEN score>=70 AND score < 85 THEN 1 ELSE 0 END) AS lev2
,SUM(CASE WHEN score>=60 AND score < 70 THEN 1 ELSE 0 END) AS lev3
,SUM(CASE WHEN score>=0 AND score < 60 THEN 1 ELSE 0 END) AS lev4
,COUNT(*) AS cnt
FROM sc
GROUP BY c
) a
ON a.c= c.c
24、查询学生平均成绩及其名次
SET @px=0;
SELECT a.s
,s.Sname
,a.avg
,a.row_num
FROM
(
-- 因为order by 在select之后而select 中的@px必须是在排好序之后再进行排名,
-- 因此只能多嵌套一个子查询
SELECT *
,@px:=IFNULL(@px,0)+1 AS row_num
FROM
(
SELECT s
,AVG(score) as avg
FROM sc
GROUP BY s
ORDER BY AVG(score) DESC
)b
) a
JOIN student s
ON s.s=a.s
ORDER BY a.row_num
25、查询各科成绩前三名的记录
SET @px=0;
SELECT s.Sname
,b.C
,b.S
,b.score
,b.row_num
FROM
(
SELECT a.C
,a.S
,a.score
,@px:=(SELECT COUNT(*) FROM sc WHERE a.c=c AND score > a.score) + 1 AS row_num
FROM sc a
)b
JOIN student s
ON b.S=s.S
WHERE b.row_num BETWEEN 1 AND 2
ORDER BY b.C,b.score DESC
26、查询每门课程被选修的学生数
SELECT c.C
,c.Cname
,a.cnt
FROM course c
JOIN
(
SELECT C
,COUNT(*) AS cnt
FROM sc
GROUP BY C
)a
ON a.C=c.C
27、查询出只有两门课程的全部学生的学号和姓名
SELECT s.S
,s.Sname
FROM student s
JOIN
(
SELECT S
FROM sc
GROUP BY S
HAVING COUNT(*)=2
)a
ON s.S=a.S
28、查询男生、女生人数
SELECT SUM(CASE WHEN Ssex='男' THEN 1 ELSE 0 END) boy
,SUM(CASE WHEN Ssex='女' THEN 1 ELSE 0 END) girl
,COUNT(*) total
FROM student s
29、查询名字中含有"风"字的学生信息
SELECT *
FROM student s
WHERE Sname LIKE '%风%'
30、查询同名同性学生名单,并统计同名人数
COUNT(expr) 属于聚合函数在select的时候会将所有数据合并到一起
因此必须多建立一个子查询
SET @px=0;
SELECT DISTINCT s1.S
,s1.Sname
,@px:=(SELECT COUNT(*) FROM student s3 WHERE s3.Sname=s1.Sname AND s3.S!=s1.S)+1 AS cnt
FROM student s1
JOIN student s2
ON s1.Sname=s2.Sname AND s1.S != s2.S
ORDER BY s1.S
31、查询1990年出生的学生名单(注:Student表中Sage列的类型是datetime)
SELECT *
FROM student s
WHERE YEAR(s.Sage)='1990'
32、查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号
SELECT b.C
,c.Cname
,b.score
,b.avg
FROM
(
SELECT C
,score
,AVG(score) avg
FROM sc
GROUP BY C
)b
JOIN course c
ON b.C = c.C
ORDER BY b.avg DESC,b.C ASC
33、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩
SELECT s.S
,s.Sname
,b.avg
FROM student s
JOIN
(
SELECT S
,AVG(score) avg
FROM sc
GROUP BY S
HAVING AVG(score) >= 85
)b
ON s.S=b.S
34、查询课程名称为"数学",且分数低于60的学生姓名和分数
SELECT s.Sname
,sc.score
FROM sc
JOIN student s
ON sc.S = s.S
WHERE EXISTS
(
SELECT C
FROM course c
WHERE c.Cname ='数学' AND sc.C=c.C AND sc.score < 60
)
35、查询所有学生的课程及分数情况;
SELECT s.Sname
,c.Cname
,sc.score
FROM student s
LEFT JOIN sc
ON s.S = sc.S
LEFT JOIN course c
ON sc.C=c.C
ORDER BY s.S
36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数;
SELECT s.Sname
,c.Cname
,sc.score
FROM student s
JOIN sc
ON sc.S=s.S
JOIN course c
ON c.C=sc.C
WHERE sc.score > 70
37、查询不及格的课程
SELECT s.Sname
,c.Cname
,sc.score
FROM student s
JOIN sc
ON s.S=sc.S
JOIN course c
ON sc.C=c.C
WHERE sc.score<60
38、查询课程编号为01且课程成绩在80分以上的学生的学号和姓名;
SELECT m.S
,m.Sname
FROM student AS m
JOIN sc
ON sc.S=m.S
WHERE sc.C='01' AND sc.score>=80
39、求每门课程的学生人数
SELECT a.C
,co.Cname
,a.cnt
FROM
(
SELECT C
,COUNT(1) AS cnt
FROM sc
GROUP BY C
) a
JOIN course co
ON a.C = co.C
40、查询选修"张三"老师所授课程的学生中,成绩最高的学生信息及其成绩
SELECT s.*
,MAX(score) AS top
FROM sc
JOIN student s
ON s.S=sc.S
WHERE EXISTS
(
SELECT c.C
FROM teacher t
JOIN course c
ON c.T=t.T
WHERE t.Tname='张三' AND sc.C=c.C
)
41、查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩
SELECT t1.s
,t1.c
,t1.score
,t2.c
,t2.score
FROM sc t1
JOIN sc t2
ON t1.s=t2.s
where t1.c>t2.c AND t1.score=t2.score
42、查询每门功课成绩最好的前两名
SET @px=0;
SELECT s.Sname
,d.Cname
,c.row_num
FROM
(
SELECT C
,S
,@px:=(SELECT COUNT(*) FROM sc b WHERE b.C=a.C AND b.score>a.score)+1 AS row_num
FROM sc a
)c
JOIN course d
ON c.C=d.C
JOIN student s
ON s.S=c.S
WHERE c.row_num BETWEEN 1 AND 2;
ORDER BY d.C
43、统计每门课程的学生选修人数(超过5人的课程才统计)。
要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
SELECT C
,COUNT(*) AS cnt
FROM sc
GROUP BY C
HAVING COUNT(*)>5
ORDER BY cnt DESC,C ASC
44、检索至少选修两门课程的学生学号
SELECT S
,COUNT(*)
FROM sc
GROUP BY S
HAVING COUNT(*) >=2
45、查询选修了全部课程的学生信息
46、查询各学生的年龄
SELECT *
,YEAR(CURDATE()) - YEAR(s.sage) AS age
FROM student s;
47、查询本周过生日的学生
SELECT *
FROM student s
WHERE YEARWEEK(date_format(s.Sage,CONCAT(YEAR(NOW()),'-%m-%d'))) = YEARWEEK(now());
48、查询下周过生日的学生
SELECT *
FROM student s
WHERE YEARWEEK(date_format(s.Sage,CONCAT(YEAR(NOW()),'-%m-%d'))) = YEARWEEK(DATE_ADD(NOW(),INTERVAL 1 WEEK));
49、查询本月过生日的学生
SELECT *
FROM student s
WHERE MONTH(s.Sage) = MONTH(now());
50、查询下月过生日的学生
SELECT *
FROM student s
WHERE MONTH(s.Sage) = MONTH(DATE_ADD(NOW(),INTERVAL 1 MONTH));
SHOW WARNINGS;