多表联查
1.内联查询
只有完全满足条件(主外键关系)的数据才能出现的结果
#1.1非等值联查 笛卡尔积 两个结果集的相乘 逻辑上有错误
select * from student,class;
#1.2等值联查
-- 查询出学生和班级信息student class 相同字段做主键那个是主表 做外键那个是子表
select * from student,class
where student.classid=class.classid and ssex='男';
-- 5张表全部联查起来stduent class sc cource teacher
select * from teacher,course,sc,student,class
where student.sid=sc.sid and class.classid=student.classid and course.cid=sc.cid and teacher.tid=course.tid and course.cid=sc.cid;
#面试题:查询出学过张三老师课程的学生信息
select * from student,course,teacher,sc
where teacher.tid=course.tid and course.cid=sc.cid and student.sid=sc.sid and teacher.tname='张三';
#多表最终跟单表一致
-- 查询每个学生的平均成绩 学生姓名,班级名称,平均成绩
select student.sname,class.classname,avg(sc.score) from student,sc,class
where student.sid=sc.sid and class.classid=student.classid
group by student.sid;
#inner join on
-- 通过第一张表结果集进行on条件匹配 适合表少,但是每张表的数据量很大,内存占用小,但是IO很高
select * from student inner join class
on student.classid=class.classid
where ssex='男';
-- 等价 只是语法上不一样
-- 笛卡尔积后进行条件筛选 非常适合表的个数多,但是每个表的数据量不大,形成的笛卡尔积也不大,io少但内存占的大(IO小 只需读一次,但是吃内存)
select * from student,class
where student.classid=class.classid and ssex='男';
-- 三表联查
select * from student
inner join class on student.classid=class.classid
inner join sc on student.sid=sc.sid;
-- 五表联查
select * from student
inner join class on student.classid=class.classid
inner join sc on student.sid=sc.sid
inner join course on course.cid=sc.cid
inner join teacher on teacher.tid=course.tid;
-- 等价 只是语法上不一样
select * from student,class,sc,course,teacher
where student.sid=sc.sid and class.classid=student.classid and course.cid=sc.cid and teacher.tid=course.tid and course.cid=sc.cid;
-- 每门课程的平均成绩 课程名称 代课老师姓名 平均成绩
select course.Cname,teacher.Tname,avg(sc.score) from course,teacher,sc
where course.tid=teacher.tid and sc.cid=course.cid
group by course.cid;-- 等价 只是语法上不一样
select ourse.Cname,teacher.Tname,avg(sc.score) from course
inner join teacher on course.tid=teacher.tid
inner join sc on sc.cid=course.cid
group by sc.cid;c
2.外联查询
#left join on 左外联
-- 主表放在左边用left 放在右边用right
-- 把较小的表放在较大的表的左边
select * from student
left join class on student.classid=class.classid;
#right join on 右外联
select * from class
right join student on student.classid=class.classid;
-- 查询出所有的学生都学过多少门课程 学生姓名 课程数
select sname,count(course.cid) from student
left join sc on sc.sid=student.sid
left join course on sc.cid=course.cid
group by student.sid;
-- 如果没有约束,它可能是错误的
-- 查找没有班级的学生
# 左连接主表放在上面 右连接主表需要倒过来
select * from student
left join class on student.classid=class.classid
where class.classid is null;-- 查找没有学生的班级
select * from student
right join class on student.classid=class.classid
where student.sid is null;
-- 既要拿到没有学生的班级,又要拿到没有班级的学生
# union 两个结果集的并集
- 去除重复 与distinct 一样
- 不同类型的字段是可以合并的
- 不同列数量的结果集不允许合并
- 起别名给第一个结果集才有用
- 库中的两个名字一样
select sname 姓名,sid 编号 from student
union
select tname,tid from teacher
-- 获取没有学生的班级和没有班级的学生的数据
select * from student
left join class on student.classid=class.classid
where class.classid is null
union
select * from student
right join class on student.classid=class.classid
where student.sid is null;
-- 获取没有班级的学生和班级和学生都有的还要获取没有学生的班级
-- 全连接
select * from student
left join class on student.classid=class.classid
union
select * from student
right join class on student.classid=class.classid
-- 不去重的并集 加上all
select * from student
left join class on student.classid=class.classid
union all
select * from student
right join class on student.classid=class.classid
3.子查询
- 所有的子查询必须用小括号括起来
- 效率极低
#where子查询
-- 查询id最大的一个学生
select * from student order by sid desc limit 1;-- 查询id最大的一个学生(子查询)
select * from student
where sid=(select max(sid) from student);
-- 查询每个班下id最大的学生(子查询)
select classname,sname,sid from student
inner join class on class.classid=student.classid
where sid in(
select max(sid) from student
group by classid);
-- 查询学过张三老师课程的学生
select * from student where sid in(
select sid from sc where cid=(
select cid from course where tid=(
select tid from teacher where tname='张三')));-- 查询没学过张三老师课程的学生 做反向过滤
select * from student where sid not in(
select sid from sc where cid=(
select cid from course where tid=(
select tid from teacher where tname='张三')));
#from子查询
查询结果将作为一张表去使用
-- 查询大于5人的班级名称和人数(不使用子查询)
select classname,count(sid) from class
inner join student on class.classid=student.classid
group by class.classid
having count(sid)>5;-- 查询大于5人的班级名称和人数(使用子查询)
select classname,人数 from class left join(
select classid,count(sid) 人数 from studentgroup by classid) t1
on class.classid=t1.classid
where 人数>5;
#exists子查询
子句有结果,父句执行,子句没结果,父句不执行
select * from teacher
where exists (select * from student where classid=5);
#any \ some all
-- 查询出一班成绩比二班最低成绩高的学生
select * from student left join sc on student.sid=sc.sid
where cid=1 and score>
(select min(score) from sc left join student on sc.sid=student.sid
where cid=2);
select distinct student.* from sc
left join student on sc.sid=student.sid
where student.classid=1 and score>any(
select score from sc
left join student on sc.sid=student.sid
where student.classid=2);select distinct student.* from sc
left join student on sc.sid=student.sid
where student.classid=1 and score>all(
select score from sc
left join student on sc.sid=student.sid
where student.classid=2);
4.结果集控制语句
IF(expr1,expr2,expr3)
expre1 条件
expre2 条件成立,显示数据
expre3 条件不成立,显示数据
-- 1:女、
-- 2:男
select tid,tname,if(tsex=1,'女','男') tsex,tbirthday,taddress from teacher;
IFNULL(expr1,expr2)
expr1 字段
expr2 当字段为null,默认值
select sid,sname,ifnull(birthday,'这个学生没有生日') bir,ssex from student;
case when then end
select tid,tname,
case tsex
when 0 then'男'
when 1 then '女'
else '保密'
end tsex,tbirthday from teacher;select tid,tname,
case
when tsex > 1 then '男'
when tsex = 1 then '女'
when tsex <1 then'未知'
end tsex,Tbirthday from teacher;
-- 查询学生的成绩 并将大于90分的用A显示,大于80分的用B显示,大于70分的用C显示,大于60分的用不及格显示
select score,
case
when score>=90 and score<=100 then 'A'
when score>=80 then 'B'
when score>=70 then 'C'
else '不及格'
end from sc
行转列 列转行
-- 统计各个分数段的人数
select '100-90' 分数段,count(score) 人数 from sc where score<=100 and score>=90
union
select '70-90' 分数段,count(score) 人数 from sc where score<=90 and score>=70
union
select '60-70' 分数段,count(score) 人数 from sc where score<=70 and score>=60
union
select '不及格' 分数段,count(score) 人数 from sc where score<60;
select '人数' 分数段,
count(case when score<=100 and score>=90 then score end) '100-90',
count(case when score<90 and score>=70 then score end) '70-90',
count(case when score<70 and score>=60 then score end) '60-70' ,
count(case when score<60 then score end )'不及格'
from sc;