一.问题
有 3 个表 STUDENT(学生表),LESSON(课程表),TIMETABLE(学生选课表)。其中
STUDENT(SNO,SNAME)代表(学号,姓名)
(CNO,CNAME,CTEACHER)代表(课号,课名,教师)
TIMETABLE(ID,SNO,CNO,SCGRADE)代表(序号,学号,课号,成绩)
问题:
-
找出没选过“黎明”老师的所有学生姓名。
-
列出 2 门以上(含 2 门)不及格学生姓名及平均成绩。
-
既学过 1 号课程又学过 2 号课所有学生的姓名。
建表代码如下
START TRANSACTION;
DROP TABLE IF EXISTS TIMETABLE;
DROP TABLE IF EXISTS STUDENT;
DROP TABLE IF EXISTS LESSON;
CREATE TABLE STUDENT
(
SNO VARCHAR(200) PRIMARY KEY,
SNAME VARCHAR(200)
)DEFAULT CHARSET = 'utf8';
CREATE TABLE LESSON
(
CNO VARCHAR(200) PRIMARY KEY,
CNAME VARCHAR(200),
CTEACHER VARCHAR(200)
) DEFAULT CHARSET = 'utf8';
CREATE TABLE TIMETABLE
(
ID INT PRIMARY KEY AUTO_INCREMENT,
SNO VARCHAR(200),
CNO VARCHAR(200),
SCGRADE VARCHAR(200),
FOREIGN KEY(SNO) REFERENCES STUDENT(SNO),
FOREIGN KEY(CNO) REFERENCES LESSON(CNO)
)DEFAULT CHARSET = 'utf8';
INSERT INTO LESSON ( CNO, CNAME, CTEACHER ) VALUES ( '1', '语文', '张');
INSERT INTO LESSON ( CNO, CNAME, CTEACHER ) VALUES ( '2', '政治', '王');
INSERT INTO LESSON ( CNO, CNAME, CTEACHER ) VALUES ( '3', '英语', '李');
INSERT INTO LESSON ( CNO, CNAME, CTEACHER ) VALUES ( '4', '数学', '赵');
INSERT INTO LESSON ( CNO, CNAME, CTEACHER ) VALUES ( '5', '物理', '黎明');
INSERT INTO STUDENT ( SNO, SNAME ) VALUES ( '1', '学生 1');
INSERT INTO STUDENT ( SNO, SNAME ) VALUES ( '2', '学生 2');
INSERT INTO STUDENT ( SNO, SNAME ) VALUES ( '3', '学生 3');
INSERT INTO STUDENT ( SNO, SNAME ) VALUES ( '4', '学生 4');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '1', '1', '40');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '1', '2', '30');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '1', '3', '20');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '1', '4', '80');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '1', '5', '60');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '2', '1', '60');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '2', '2', '60');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '2', '3', '60');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '2', '4', '60');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '2', '5', '40');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '3', '1', '60');
INSERT INTO TIMETABLE ( SNO, CNO, SCGRADE ) VALUES ( '3', '3', '80');
commit;
二.解析
问题1
找出没选过“黎明”老师的所有学生姓名。
思路:首先找出所有选过”黎明“老师课程的学生,将其存储在视图LimingStudent中。然后用STUDENT表与LimingStudent进行比对,即可找出剩下没选过”黎明“老师课程的学生。
step1: 利用表连接,将timetable中的课程号换成上课老师的姓名
select t.id,t.sno,l.cteacher,t.scgrade from timetable t join lesson l on t.cno = l.cno order by id;
step2: 找到所有选过“黎明”老师课程的学生编号
select a.sno from (select t.id,t.sno,l.cteacher,t.scgrade from timetable t join lesson l on t.cno = l.cno order by id) a where cteacher = '黎明';
PS:上面语句中的表a 即为step1中的表
step3: 将step2中的查询结果存储在视图LimingStudent中
create view LimingStudent as select a.sno from (select t.id,t.sno,l.cteacher,t.scgrade from timetable t join lesson l on t.cno = l.cno order by id) a where cteacher = '黎明';
step4: 将LimingStudent中的学生编号与Student中的学生编号进行比对,找到没选过“黎明”课的学生.
select s.sname from student s join LimingStudent on s.sno not in (select sno from LimingStudent);
step5: 由于表连接是两个表的所有表项按照笛卡尔积一一比对,所以结果有重复。因此不要忘了要去重
select distinct s.sname from student s join LimingStudent on s.sno not in (select sno from LimingStudent);
最后记得删除视图!
完整代码为
create view LimingStudent as select a.sno from (select t.id,t.sno,l.cteacher,t.scgrade from timetable t join lesson l on t.cno = l.cno order by id) a where cteacher = '黎明';
select distinct s.sname from student s join LimingStudent on s.sno not in (select sno from LimingStudent);
drop view LimingStudent;
问题2
列出 2 门以上(含 2 门)不及格学生姓名及平均成绩。
思路: 找到两门以上不及格的学生编号,而后利用表连接找出学生姓名,利用分组函数avg找出平均成绩。
step1: 找到挂过科的学生的学生编号
select sno,cno,scgrade from timetable where scgrade < 60;
step2: 将step1中的表按照sno进行分组,找出每个学生不及格的课程数目
select t.sno,count(t.cno)count from(select sno,cno,scgrade from timetable where scgrade < 60) t group by t.sno;
step3: 利用step2中的表,找到挂科数大于2门的学生编号
select t1.sno from (select t.sno,count(t.cno) count from(select sno,cno,scgrade from timetable where scgrade < 60) t group by t.sno) t1 where t1.count>=2;
step4: 将step3中的结果创建一个视图存储起来。
create view view1 as select t1.sno from (select t.sno,count(t.cno) count from(select sno,cno,scgrade from timetable where scgrade < 60) t group by t.sno) t1 where t1.count>=2;
step5: 利用表连接查询对应的学生姓名
select s.sname,v.sno from student s join view1 v on s.sno = v.sno;
step6: 利用分组函数avg计算出大于2门不及格的学生的平均成绩。注意sql语句的where部分,要用in来判断,而不能用=,如果view1中的数据大于1,用=会报错。
select sno,avg(scgrade) avg from timetable where sno in (select sno from view1) group by sno;
step7: 将step5的结果与step6的结果利用表连接合并
select t1.sname ,t2.avg from ( select s.sname,v.sno from student s join view1 v on s.sno = v.sno) t1 join ( select sno,avg(scgrade) avg from timetable where sno in (select sno from view1) group by sno) t2 on t1.sno = t2.sno;
完整代码
create view view1 as select t1.sno from (select t.sno,count(t.cno) count from(select sno,cno,scgrade from timetable where scgrade < 60) t group by t.sno) t1 where t1.count>=2;
select t1.sname ,t2.avg from ( select s.sname,v.sno from student s join view1 v on s.sno = v.sno) t1 join ( select sno,avg(scgrade) avg from timetable where sno in (select sno from view1) group by sno) t2 on t1.sno = t2.sno;
drop view view1;
问题3
既学过 1 号课程又学过 2 号课所有学生的姓名。
思路:首先找出学过1号课程的学生姓名,然后找出学过2号课程的学生姓名,最后取交集。
step1: 先在timetable表中找到选1号课程学生的学生编号
select sno,cno from timetable where cno = 1;
step2: 利用表连接,找到选1号课程对应的学生姓名
select s.sname,t.cno from (select sno,cno from timetable where cno = 1) t join student s on t.sno = s.sno;
step3: 将step2的结果存储在一个视图中
create view view1 as select s.sname,t.cno from (select sno,cno from timetable where cno = 1) t join student s on t.sno = s.sno;
step4: 按照上面的步骤选出选择2号课程的学生姓名,并存储在一个视图中
create view view2 as select s.sname,t.cno from (select sno,cno from timetable where cno = 2) t join student s on t.sno = s.sno;
step5: 将两个视图连接查询,找出同时存在于两个视图中的学生姓名
select v1.sname from view1 v1 join view2 v2 on v1.sname = v2.sname;
完整代码
create view view1 as select s.sname,t.cno from (select sno,cno from timetable where cno = 1) t join student s on t.sno = s.sno;
create view view2 as select s.sname,t.cno from (select sno,cno from timetable where cno = 2) t join student s on t.sno = s.sno;
select v1.sname from view1 v1 join view2 v2 on v1.sname = v2.sname;
drop view view1;
drop view view2;