Bootstrap

Mysql高级DQL数据查询

多表联查


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 student

group 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;

;