MySQL3(多表联查 子查询 流程控制函数,语句 Sql 执行顺序 学生选课表练习)

目录

一、多表联查

1. 等值查询和非等值查询

等值查询 --- 主外键相等

​编辑非等值查询

2. 连接查询

​编辑

内连接

​编辑

左外连接

​编辑

右外连接

3. UNION

二、子查询

1. where 型子查询:

2. from型子查询:

3. exists型子查询:

4. any, some, all子查询:

any 子查询

all 子查询 

5. 流程控制函数,语句 

IF()

IFNULL()

case when then end语句

简单Case函数

Case搜索函数

6. 常见题

数据表的行转列,列转行

三、Sql 执行顺序

四、

创建表:

1. 查询” 01 “课程比” 02 “课程成绩高的学生的信息及课程分数

2.  查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩 

3. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )

4. 查询「李」姓老师的数量

5. 查询学过「张三」老师授课的同学的信息

6. 查询没有学全所有课程的同学的信息

7. 查询和” 01 “号的同学学习的课程完全相同的其他同学的(⭐)

8. 查询没学过”张三”老师讲授的任一门课程的学生姓名

9. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

10. 检索” 01 “课程分数小于 60,按分数降序排列的学生信息

11. 查询各科成绩最高分、最低分和平均分,以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率(及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)。


一、多表联查

        多表联查可以通过连接运算实现,即将多张表通过主外键关系关联在一起进行查询。下图提供了多表联查 时用到的数据库表之间的关系。

总结:多表连接查询,所谓’连接’—通过一个列名去寻找对应的另外一个列名

笛卡尔积        

        笛卡尔乘积是指在数学中,两个集合X和Y的笛卡尔积(Cartesian product),又称直积,表示为X × Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员。

        通俗理解就是一个集合中的所有元素与另外一个集合中的所有元素的所有组合。需要注意有先后顺序。        

集合A={a,b}, B={0,1,2},笛卡尔积结果为:

A×B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}
B×A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)}

可以简单理解为两个集合的乘积

1. 等值查询和非等值查询

语法:

等值查询:SELECT * FROM 表1,表2 WHERE 表1.字段1 = 表2.字段2...

非等值查询:SELECT * FROM 表1,表2


其中:
• 与单表查询类似,都是SELECT语句;
• 把多个表放到FROM后,并用逗号隔开;
• 可使用AS关键字取别名,便于引用;
• 如无重名查询字段则可省略数据表的指定。

等值查询 --- 主外键相等

等值连接指的是,多表查询语句中的连接条件使用的是等号。

-- 等值查询 -- 主外键相等
-- 内联查询
select * from student,class 
where student.classid = class.classid

练习:

-- 查询出学过张三老师课程的学生信息
select student.* from student,class,sc,course,teacher 
where student.classid = class.classid 
and student.sid = sc.sid and sc.cid = course.Cid
and course.tid = teacher.tid and teacher.tname = '张三'

非等值查询

-- 查询学生和班级信息
-- 笛卡尔积
select * from student , class

2. 连接查询

又称多表查询,当查询的字段涉及多个表的时候,就要用到连接查询

  • INNER JOIN:在表中至少一个匹配时,则返回记录。
  • LEFT JOIN:从左表(表1)中返回所有的记录,即便在右 (表2)中没有匹配的行。
  • RIGHT JOIN:从右表(table_2)中返回所有的记录,即便 在左(table_1)中没有匹配的行。

语法:

SELECT * FROM 表1 LEFT|right|INNER JOIN 表2 ON 条件


内连接

-- 方式二
-- 内联查询
inner JOIN  ON

select * from student 
inner join class on student.classid = class.classid

相当于查询中间绿色的部分

-- 找出所有性别为女性的学生及其对应的班级信息。
-- 等值  -- 笛卡而积
select * from student,class 
where student.classid = class.classid 
and student.ssex = '女'

-- inner join
select * from student 
inner join class on student.classid = class.classid
where student.ssex = '女'


select * from student 
inner join class on student.classid = class.classid 
inner join sc on student.sid = sc.sid 
where student.ssex = '女'


左外连接

-- 外联查询
-- 主查表
-- left join on 
select * from student 
left join  class on student.classid = class.classid

以学生表为主表相当于查询中间绿色的交集部分的数据。

以学生表为主表相当于查询黄色部分

select * from student 
left join class on student.classid = class.classid
where class.classid is null;

以班级表为主表相当于查询紫色部分

select * from  class
left join student on student.classid = class.classid
where student.sid is null;

右外连接

-- right join on 
select student.*,class.* from class 
right join student on student.classid = class.classid 

以班级表为主表相当于查询中间绿色的交集部分的数据。

3. UNION

union是求两个查询的并集。 union合并的是结果集,不区分来自于哪一张表,所以可以合并多张表查询出来的数据。

语法:

select A.field1 as f1, A.field2 as f2 from <table1> A 
union 
(select B.field3 as f1, field4 as f2 from <table2> B)
order by 字段 desc/asc

注意:

  1. 列名不一致时,会以第一张表的表头为准,并对其栏目。
  2. 会将重复的行过滤掉。
  3. 如果查询的表的列数量不相等时,会报错。
  4. 在每个子句中的排序是没有意义的,mysql在进行合并的时候会忽略掉。
  5. 如果子句中的排序和limit进行结合是有意义的。
  6. 可以对合并后的整表进行排序
-- 数据的并集
-- union 
-- 1. distinct 相同的去重
-- 2. 不同数据类型可以合并
-- 3. 不同结果集要想合并必须有同等数量的字段
-- 4. 表头信息始终是第一个结果集的表头
-- 找到库中所有人的名字
select tname,tsex,taddress from teacher
union
select sname,ssex,classid from student
union 
select sid,cid,score from sc

 找出所有班级的学生

select * from student 
left join class on student.classid = class.classid
union
select student.*,class.* from  class
left join student on student.classid = class.classid

二、子查询

子查询,又叫内部查询

1. where 型子查询:

查询id最大的一个学生(使用排序+分页实现)

查询id最大的一个学生(使用where子查询实现)

# 子查询
-- 所有的子查询必须用小括号括起来
-- where子查询

-- 查询id最大的一个学生(使用排序+分页实现)
select * from student order by sid desc limit 1

-- 查询id最大的一个学生(使用where子查询实现)

select * from student 
where sid = (
	select max(sid) from student
)

 查询每个班下id最大的学生(使用where子查询实现)

2. from型子查询:

        把内层的查询结果当成临时表,供外层sql再次查询。查询结果集可以当成表看待。 临时表要使用一个别名。

-- 查询大于5人的班级名称和人数(不使用子查询)

-- 查询大于5人的班级名称和人数(使用from型子查询)

-- from 子查询
-- 查询大于5人的班级名称和人数(不使用子查询)

-- 查询大于5人的班级名称和人数(使用from型子查询)

select class.classname,t1.人数 from class inner join 
(select classid, count(*) 人数 
	from student group by classid)t1
on class.classid  = t1.classid
where 人数 > 5

3. exists型子查询:

        把外层sql的结果,拿到内层sql去测试,如果内层的sql成立,则该行取出。内层查询是exists后的查询。

从学生表中取出学生的信息,(如果该性别下没有外星人学生则不显示)

-- exists 子查询
select * from teacher where exists (
		select * from student where ssex = '外星人'
)

4. any, some, all子查询:

any 子查询

表示满足其中任意一个条件
假设any内部的查询语句返回的结果个数是三个,
    如:result1,result2,result3,那么,
        select ...from ... where a > any(...);
    ->相当于:
        select ...from ... where a > result1 or a > result2 or a > result3;

some 是 any的别名,所以用法是一样的,可以替代使用 现在都用any

题:查询出一班成绩比二班最低成绩高的学生

-- some any all 子查询

-- some/ any

-- 题:查询出一班成绩比二班最低成绩高的学生
select DISTINCT student.* from sc 
inner join student on sc.sid = student.sid
where student.classid = 1 and score >
(select min(score) from sc 
inner join student on sc.sid = student.sid
where student.classid = 2)


select DISTINCT student.* from sc 
inner join student on sc.sid = student.sid
where student.classid = 1 and (score > 70.0 
or score > 60.0 or score > 80.0 or score > 50 
or score >30 or score > 20 or score >31 or score >34)

select DISTINCT student.* from sc 
inner join student on sc.sid = student.sid
where student.classid = 1 and score > any(
	select score from sc 
	inner join student on sc.sid = student.sid
	where student.classid = 2
);

all 子查询 

表示满足其中所有条件条件,ALL关键字与any关键字类似,只不过上面的or改成and。
假设any内部的查询语句返回的结果个数是三个,
        select ...from ... where a > all(...);
->
        select ...from ... where a > result1 and a > result2 and a > result3;

题:查询出一班成绩比二班最高成绩高的学生

-- all 子查询
-- 题:查询出一班成绩比二班最高成绩高的学生
select DISTINCT student.* from sc 
inner join student on sc.sid = student.sid
where student.classid = 1 and score >
(select max(score) from sc 
inner join student on sc.sid = student.sid
where student.classid = 2)

select DISTINCT student.* from sc 
inner join student on sc.sid = student.sid
where student.classid = 1 and (score > 70.0 
and score > 60.0 and score > 80.0 and score > 50 
and score >30 and score > 20 and score >31 and score >34)


select DISTINCT student.* from sc 
inner join student on sc.sid = student.sid
where student.classid = 1 and score > all(
	select score from sc 
	inner join student on sc.sid = student.sid
	where student.classid = 2
);

5. 流程控制函数,语句 

        流程处理函数可以根据不同的条件,执行不同的处理流程,可以在SQL语句中实现不同的条件选择。MySQL中的流程处理函数主要包括IF()、IFNULL()和CASE()函数。

函数用法
IF(value,value1,value2)如果value的值为TRUE,返回value1,否则返回value2
IFNULL(value1, value2)如果value1不为NULL,返回value1,否则返回value2
CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2 …… [ELSE resultn] END相当于Java的if...else if...else...
CASE expr WHEN 常量值1 THEN 值1 WHEN 常量值1 THEN 值1…… [ELSE 值n] END相当于Java的switch...case...

IF()

# 特殊的控制语句
IF(expr1,expr2,expr3)
-- expr1 表达式
-- 成立 输出expr2
-- 不成 输出expr3
select tid,tname,if(tsex=1,'男','女'),tbirthday,
taddress from teacher


IFNULL()

IFNULL(expr1,expr2)
-- expr1 字段名 -- 的值为null时则显示默认值
-- expr2 默认值
select sid,sname,ifnull(birthday,'这个同学没有生日'),
ssex,classid from student


case when then end语句

Case具有两种格式。简单Case函数和Case搜索函数。

简单Case函数
-- 简单case
-- case when then end 
select tid ,tname, 
case tsex
	when 1 then '男'
	when 0 then '女'
	when 2 then '保密'
	else '未知'
end, 
taddress from teacher

Case搜索函数
-- 搜索case
select tid, tname, 
case 
	when tsex > 1 then '男'
	when tsex < 1 then '女'
	when tsex = 1 then '保密'
end, taddress from teacher

练习题: 查询学生的成绩, 并将大于90分的用A显示, 大于80分的用B显示, 大于70分的用C显示, 大于60分的用D显示, 小于60分的显示不及格

select sid,score,
case 
	when score >=60 and score <70 then 'D'
	when score >=90  then 'A'
	when score >=80 and score <90 then 'B'
	when score >=70 and score <80 then 'C'
	when score <60 then '不及格'
end ,cname
from sc,course where sc.cid = course.cid

6. 常见题

数据表的行转列,列转行

分数段  人数
100-90   5 
90-70    15
70-60     2
60以下    3

分数段   100-90   90-70   70-60  60一下
人数        5       15      2      3
 

-- 数据表的行转列,列转行
分数段  人数
100-90   5 
90-70    15
70-60     2
60以下    3

select '100-90' 分数段, count(*) 人数 from sc where score >= 90
union
select '90-70' 分数段, count(*) 人数 from sc where score >= 70 and score < 90


分数段   100-90   90-70   70-60  60一下
人数        5       15      2      3

select 
'人数' 分数段,
count(case when score >= 90 then score end) '100-90',
count(case when score >= 70 and score <90 then score end) '90-70',
count(case when score >= 60 and score <70 then score end) '70-60',
count(case when score < 60 then score end) 不及格
 from sc

三、Sql 执行顺序

  1. 系统(客户端)访问 MySQL 服务器前,做 的第一件事就是建立 TCP 连接。
  2. Caches & Buffers: 查询缓存组件
  3. SQL Interface: SQL接口 接收用户的SQL命 令,并且返回用户需要查询的结果。比如 SELECT ... FROM就是调用SQL Interface MySQL支持DML(数据操作语言)、DDL (数据定义语言)、存储过程、视图、触发器、 自定 义函数等多种SQL语言接口
  4. Parser: 解析器:在解析器中对 SQL 语句进行 语法分析、语义分析。
  5. Optimizer: 查询优化器
  6. 存储引擎
  7. 文件系统
  8. 日志系统

四、

学生表:Student(编号sid,姓名sname,生日birthday,性别ssex,班级 classid)

课程表:Course(课程编号cid,课程名称cname,教师编号tid)

成绩表:Sc(学生编号sid,课程编号cid,成绩score)

教师表:Teacher(教师编号tid,姓名tname)

班级表:Class (班级编号 classid,班级名称 classname)

创建表:

学生表 Student

create table Student(Sid int primary key, Sname varchar(10), birthday datetime, Ssex varchar(10), classid int);
insert into Student values('1' , '赵雷' , '1990-01-01' , '男', '1');
insert into Student values('2' , '钱电' , '1990-12-21' , '男', '2');
insert into Student values('3' , '孙风' , '1990-05-20' , '男', '1');
insert into Student values('4' , '李云' , '1990-08-06' , '男', '2');
insert into Student values('5' , '周梅' , '1991-12-01' , '女', '1');
insert into Student values('6' , '吴兰' , '1992-03-01' , '女', '2');
insert into Student values('7' , '郑竹' , '1989-07-01' , '女', '1');
insert into Student values('8' , '王菊' , '1990-01-20' , '女', '2');

成绩表 SC

create table SC(Sid int, Cid int, score decimal(18,1));
insert into SC values('1' , '1' , 80);
insert into SC values('1' , '2' , 90);
insert into SC values('1' , '3' , 99);
insert into SC values('2' , '1' , 70);
insert into SC values('2' , '2' , 60);
insert into SC values('2' , '3' , 80);
insert into SC values('3' , '1' , 80);
insert into SC values('3' , '2' , 80);
insert into SC values('3' , '3' , 80);
insert into SC values('4' , '1' , 50);
insert into SC values('4' , '2' , 30);
insert into SC values('4' , '3' , 20);
insert into SC values('5' , '1' , 76);
insert into SC values('5' , '2' , 87);
insert into SC values('6' , '1' , 31);
insert into SC values('6' , '3' , 34);
insert into SC values('7' , '2' , 89);
insert into SC values('7' , '3' , 98);

课程表 Course

create table Course(Cid int primary key,Cname varchar(10),Tid varchar(10));
insert into Course values('1' , '语文' , '2');
insert into Course values('2' , '数学' , '1');
insert into Course values('3' , '英语' , '3');

教师表 Teacher

create table Teacher(
	Tid int primary key auto_increment,
	Tname varchar(10),
	Tsex TINYINT default 1,
	Tbirthday date,
	Taddress varchar(255),
	Temail varchar(255),
	Tmoney DECIMAL(20,2)

);
insert into Teacher values('1' , '张三',1,'1988-1-15','陕西咸阳','zhangsan@qq.com',3000.00);
insert into Teacher values('2' , '李四',0,'1992-5-9','陕西宝鸡','lisi@qq.com',4000.00);
insert into Teacher values('3' , '王五',1,'1977-7-1','山西太原','wangwu@qq.com',5000.00);

班级表 Class

create table Class(classid int primary key, classname varchar(20));
insert into Class values('1', '一班');
insert into Class values('2', '二班');

1. 查询” 01 “课程比” 02 “课程成绩高的学生的信息及课程分数

💡想法

-- 1.	查询” 01 “课程比” 02 “课程成绩高的学生的信息及课程分数
select student.*,sc01.score 课程01分数,sc02.score 课程02分数 from student
inner join sc sc01 on sc01.Sid=student.Sid and sc01.cid ='01'
inner join sc sc02 on sc02.Sid=student.Sid and sc02.cid ='02'
where sc01.score>sc02.score;

参考答案

1.	查询” 01 “课程比” 02 “课程成绩高的学生的信息及课程分数
select * from student 
inner join 
(select * from sc where cid = 1)t1 on student.sid = t1.sid
inner join 
(select * from sc where cid = 2)t2 on student.sid = t2.sid
where t1.score > t2.score

2.  查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩 

💡想法

-- 2. 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩
select Student.Sid,Sname,t1.平均成绩 from student
inner join (select Sid,avg(score) 平均成绩 from sc group by Sid)t1
on t1.Sid = student.Sid
where 平均成绩 >= 60;

参考答案

2. 查询平均成绩大于等于 60 分的同学的
学生编号和学生姓名和平均成绩
select  sc.sid,sname,avg(score) from sc,student
where sc.sid = student.sid
 group by sc.sid having avg(score) >= 60

3. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )

💡想法 这个有问题 没有统计出来null值

-- 3. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )
select Student.Sid,Sname,count(sc.Cid) 选课总数,sum(sc.score) 总成绩 from student
inner join sc on sc.Sid = student.Sid
group by sc.Sid;

参考答案

3. 查询所有同学的学生编号、学生姓名、选课总数、
所有课程的总成绩(没成绩的显示为 null )
select student.sid,sname,count(cid),sum(score) from student
left join sc on  student.sid = sc.sid
 group by sid

4. 查询「李」姓老师的数量

💡想法 参考答案

-- 4. 查询「李」姓老师的数量
select count(*) from teacher where Tname like '李%';

5. 查询学过「张三」老师授课的同学的信息

💡想法 参考答案

-- 5. 查询学过「张三」老师授课的同学的信息
select student.* from student,class,sc,course,teacher 
where student.classid = class.classid 
and student.sid = sc.sid and sc.cid = course.Cid
and course.tid = teacher.tid and teacher.tname = '张三';

6. 查询没有学全所有课程的同学的信息

💡想法 不太对

-- 6. 查询没有学全所有课程的同学的信息
select student.* from student 
inner join sc on sc.Sid = student.Sid
group by sc.Sid having count(sc.Cid)<(
		select count(*) from course
);

参考答案

6. 查询没有学全所有课程的同学的信息
select student.* from student 
left join sc on student.sid = sc.sid 
group by student.sid 
having count(cid) < (select count(*) from course)

7. 查询和” 01 “号的同学学习的课程完全相同的其他同学的(⭐)

💡想法 不对

-- 7. 查询和” 01 “号的同学学习的课程完全相同的其他同学的
select student.* from student 
inner join sc on sc.Sid = student.Sid 
and Cid in (select Cid from sc where Sid =01)
group by sc.Sid;

参考答案

7. 查询和” 01 “号的同学学习的课程完全相同的其他同学的
-- 科目内容 (圈定)1,2,3  1,2  1,2,4  1,4
-- 科目数量
select student.* from student inner join sc on student.sid = sc.sid
where student.sid not in(
	select sid from sc where cid not in
	(select cid from sc where sid = 1)
)
group by student.sid 
having count(cid) = (select count(*) from sc where sid = 1)

8. 查询没学过”张三”老师讲授的任一门课程的学生姓名

💡想法参考答案

-- 8. 查询没学过”张三”老师讲授的任一门课程的学生姓名
select Sname from student 
where Sid not in(
  select distinct Sid from sc where Cid=(
    select Cid from course where Tid=(
			select Tid from teacher where Tname='张三'
)));

9. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

💡想法


-- 9. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
select student.Sid,Sname,avg(sc.score)from student 
 inner join sc on student.sid = sc.sid
where score<60 group by student.sid
having count(distinct cid)  >=2;

参考答案

9. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
select sc.sid,sname,avg(score) 
from sc,student 
where score < 60 and sc.sid = student.sid
group by sc.sid 
having count(*)>=2 

10. 检索” 01 “课程分数小于 60,按分数降序排列的学生信息

💡想法

-- 10. 检索” 01 “课程分数小于 60,按分数降序排列的学生信息
-- select * from sc where cid = 01 and score < 60;

select student.* ,t1.score from student 
inner join(select * from sc where cid = 01 and score < 60)t1
on student.Sid = t1.Sid order by t1.score desc;

参考答案


10. 检索” 01 “课程分数小于 60,按分数降序排列的学生信息
select  student.*
from student
inner join sc on  student.sid = sc.sid
where  score < 60 and cid = 1
order by score desc

11. 查询各科成绩最高分、最低分和平均分,以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率(及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)。

💡想法

-- 11. 查询各科成绩最高分、最低分和平均分,以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率(及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)。
select sc.cid,cname,max(sc.score) 最高分,min(sc.score) 最低分,avg(sc.score) 平均分 ,
       ((select count(sid) from sc where score>=60 and sc.cid=course.cid )/(select count(sid) from sc where sc.cid=course.cid)) 及格率,
			 ((select count(sid) from sc where score>=70 and score <80  and sc.cid=course.cid )/(select count(sid) from sc where sc.cid=course.cid)) 中等率,
			 ((select count(sid) from sc where score>=80 and score <90 and sc.cid=course.cid )/(select count(sid) from sc where sc.cid=course.cid)) 优良率,
			 ((select count(sid) from sc where score>=90 and sc.cid=course.cid )/(select count(sid) from sc where sc.cid=course.cid)) 优秀率
from sc
inner join course  on sc.cid = course.cid
group by course.cid;

参考答案

11. 查询各科成绩最高分、最低分和平均分,
以如下形式显示:课程 ID,课程 name,
最高分,最低分,平均分,及格率,中等率,优良率,优秀率
(及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)。

select sc.cid,cname,max(score),min(score),avg(score),
count(case when score >= 60 then score end)/count(*) 及格率,
count(case when score >= 70 and score <80 then score end)/count(*) 中等率,
count(case when score >= 80 and score <90 then score end)/count(*) 优良率,
count(case when score >= 90 then score end)/count(*) 优秀率
from sc, course 
where sc.cid = course.cid 
group by sc.cid 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冯诺依曼转世

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值