day05 连表查询与子查询


day05 连表查询与子查询

昨日内容回顾

表关系之一对一

换位思考之后得出两边都是不可以
	要么是没有关系,要么是一对一
	一对一的表关系外键虽然建在哪个都可以,但是建议建在查询频率多的表上
	
# 外键其实是强耦合,有时候表热别多的时候并不一定建外键,而是用过代码建立逻辑意义上的关系

操作表的SQL语句补充

1、修改表名称
	rename table m1 to m2;
	rename table m1 to m2,m3 to m4;
	
2、添加表字段
	alter table m1 add age int;
	
3、删除字段名
	alter table m1 drop age;
	
4、修改字段名和字段类型
	alter table m1 modify age int;
	alter table change age age1 int;

复制表

# SQL语句的查询结果其实也可以看成一张虚拟表
create table m1 select * from user;
create table m1 select * from user where 1=2;
create teble m1 like user;

表查询关键字

select
from
where
group by
reder by
having
distinct

聚合函数

max
min
sum
count
avg

补充知识

1、起别名
	as语法:给字段和表起名称
	
2、group_concat:拼接分组之后的字段

3、concat:拼接分组之前的字段

4、concat_ws:多个连接符相同情况下

今日内容概要

  • 表查询关键字补充
  • 多表查询两种方法
  • 可视化软件
  • 多表查询练习题

    今日内容

表查询关键字补充

1、limlt:分页
	# 只跟一个数字
	select * from emp limit 5;  # 从头开始展示n行数据
	
	# 只跟两行数字
	select * from emp limit 5,5; # 第一个是起始位置,第二个是展示n行数据
	
	案例:求薪资最高的员工所有数据
        1.先按照薪资降序排序
        2.在使用limit限制取一行
        select * from emp order by salary desc limit 1;
        
2、regexp:正则表达式
	select * from emp where name regexp '^j.*(n|y)$'
	
3、exists:是否存在
    EXISTS关字键字表示存在。在使用EXISTS关键字时,内层查询语句不返回查询的记录,
    而是返回一个真假值,True或False。
    当返回True时,外层查询语句将进行查询
    当返回值为False时,外层查询语句不进行查询。
    select * from emp  # 存在执行这个
        where exists
        (select id from emp where id > 3);  # 先验证这条数据是否存,存在执行上边

    select * from emp
        where exists
        (select id from emp where id > 250); # 验证不存在,报错

多表查询两种方法之准备

# 数据准备
#建表
create table dep(
    id int primary key auto_increment,
    name varchar(20) 
);

create table emp(
    id int primary key auto_increment,
    name varchar(20),
    sex enum('male','female') not null default 'male',
    age int,
    dep_id int
);

#插入数据
insert into dep values
(200,'技术'),
(201,'人力资源'),
(202,'销售'),
(203,'运营');

insert into emp(name,sex,age,dep_id) values
('jason','male',18,200),
('tony','female',48,201),
('kevin','male',18,201),
('nick','male',28,202),
('owen','male',18,203),
('jerry','female',18,204);

多表查询方法之连表操作

# 连表操作
	先将查询涉及到的表拼接成一张大表 之后基于单表查询
 
# 笛卡尔积:把所有的数据全部对一遍
	select * from emp,dep;  # 错误
    select * from emp,dep where dep_id=id;  # 表意不明确
    select emp.name,dep.name from emp,dep where emp.dep_id=dep.id; # 加上哪个表的字段名
    
"""
涉及到多表操作的时候 为了避免表字段重复 
	需要在字段名的前面加上表名限制
"""
# 上述操作并不是合理的连表操作,因为效率太差

连表操作关键字:
    1、inner join 内连接:只连接两表中都存在(有对应关系)的数据
       select emp.name,dep.name from emp inner join dep on emp.dep_id=dep.id;
        
    2、left join 左连接:以左表为基准展示左表所有的数据没有对应则NULL填充
    	select * from emp left join dep on emp.dep_id = dep.id;
        
    3、right join 右连接:以右表为基准展示右表所有的数据没有对应则NULL填充
    	select * from emp right join dep on emp.dep_id = dep.id;
        
    4、union 全连接:展示左右两表中所有的数据没有对应则NULL填充
    	select * from emp left join dep on emp.dep_id = dep.id
        union
        select * from emp right join dep on emp.dep_id = dep.id;

多表查询方法之子查询

# 子查询:其实就是分步操作
	将一张表的查询结果当做另外一条SQL语句的查询条件
	
1、查询部门是技术或者人力资源的员工信息
	(1)、先查询技术和人力资源的部门标号
		select id from dep where name in ('技术','人力资源');
	(2)、根据部门编号去员工表中筛选对应的员工数据
		select * from emp where dep_id in (200,201);
 # (3)、将SQL语句括号起来即可充当查询条件
 	select * from emp where dep_id in (select id from dep where name in ('技术','人力资源'));

可视化软件

为了提高开发的效率 但是不能太过于依赖该软件
其实这些软件的底层还是执行的SQL语句来操作数据库的

该软件是收费的 但是有很多破解版本 自我百度下载即可(正式版只能使用14天)

1.链接数据库
2.创建数据库
3.创建表
4.创建数据
5.创建外键
6.逆向数据库到模型
7.转储SQL文件

多表查询练习题

练习题

1、查询所有的课程的名称以及对应的任课老师姓名

2、查询学生表中男女生各有多少人

3、查询物理成绩等于100的学生的姓名

4、查询平均成绩大于八十分的同学的姓名和平均成绩

5、查询所有学生的学号,姓名,选课数,总成绩

6、 查询姓李老师的个数

7、 查询没有报李平老师课的学生姓名

8、 查询物理课程比生物课程高的学生的学号

9、 查询没有同时选修物理课程和体育课程的学生姓名

10、查询挂科超过两门(包括两门)的学生姓名和班级
、查询选修了所有课程的学生姓名

12、查询李平老师教的课程的所有成绩记录
 
13、查询全部学生都选修了的课程号和课程名

14、查询每门课程被选修的次数

15、查询之选修了一门课程的学生姓名和学号

16、查询所有学生考出的成绩并按从高到低排序(成绩去重)

17、查询平均成绩大于85的学生姓名和平均成绩

18、查询生物成绩不及格的学生姓名和对应生物分数

19、查询在所有选修了李平老师课程的学生中,这些课程(李平老师的课程,不是所有课程)平均成绩最高的学生姓名

20、查询每门课程成绩最好的前两名学生姓名

21、查询不同课程但成绩相同的学号,课程号,成绩

22、查询没学过“叶平”老师课程的学生姓名以及选修的课程名称;

23、查询所有选修了学号为1的同学选修过的一门或者多门课程的同学学号和姓名;

24、任课最多的老师中学生单科成绩最高的学生姓名

习题1:查询所有的课程的名称以及对应的任课老师姓名

# 解题思路:大象放冰箱
	1.先写思路在写SQL语句
	2.查看本题需要用到哪些表。
	3.一步步查询,不可一步就完成

1、查询所有的课程的名称以及对应的任课老师姓名
	# 1.本题需要用到course、teacher两张表
	# 2.查询结果肯定来源于这两张表,得出结论先连表:inner join
	步骤1:先连表查询
		select * from course inner join teacher on course.teacher_id=teacher.tid;
		
	步骤2:只查询需要的字段:course.cname、teacher.teacher,再美化一下。
 	select course.cname,teacher.tname from course inner join teacher on course.teacher_id=teacher.tid;
 	

习题2:查询平均成绩大于八十分的同学的姓名和平均成绩

2、查询平均成绩大于八十分的同学的姓名和平均成绩
	# 1.本题需要用到score、student两张表
	# 2.解题思路:先查询平均成绩大于八十分的同学id,然后在连表
	步骤1: 按照学生id分组,之后求每个学生的平均成绩
		select student_id,avg(num) as avg_num from score group by student_id;
		
	步骤2:在使用having过滤出平均成绩大于80分的学生id  # 给聚合函数起一个别名,要不然多表查询拿不到数据
		select student_id,avg(num) as avg_num from score group by student_id having avg(num)>80;
		
	步骤3:用子列表把查询到的表跟student表,按照学生id拼接到一起
		select student.sname,m1.avg_num from student inner join (select student_id,avg(num) as avg_num from score group by student_id having avg(num)>80) as m1 on student.sid=m1.student_id;
		
	步骤4:美化SQL语句
SELECT
	student.sname,
	m1.avg_num 
FROM
	student
	INNER JOIN ( SELECT student_id, avg( num ) AS avg_num FROM score GROUP BY student_id HAVING avg( num )> 80 ) AS m1 ON student.sid = m1.student_id;

习题3:查询没有报李平老师课的学生姓名

# 本题需要用到的表:teacher、course、score、student
# 结题思路:
	1、先查询李平老师带哪几门课程,
	2、然后在去成绩表中查询上了李平老师课程的学生id
	3、利用not in 查询出没有选李平老师课程的学生姓名


# 1.先查询李平老师带的几门课程、涉及的表有teacher、course
select course.cid from teacher inner join course on teacher.tid=course.teacher_id where teacher.tname in ('李平老师') 

# 2.在course中查询上了李平老师课程的学生id
select distinct score.student_id from score inner join (select course.cid from teacher inner join course on teacher.tid=course.teacher_id where teacher.tname in ('李平老师')) as m1 on score.course_id=m1.cid

# 3.再用student表查询没报李平老师课程的id
select sname from student where sid not in (select distinct score.student_id from score inner join (select course.cid,teacher.tname from teacher inner join course on teacher.tid=course.teacher_id where teacher.tname in ('李平老师')) as m1 on score.course_id=m1.cid)

习题4:查询没有同时选修物理课程和体育课程的学生姓名

# 查询没有同时选修物理课程和体育课程的学生姓名

# 1.先查询出选修物理课程和体育课程的课程
select cid from course where cname in ('物理','体育')

# 2.用in查询出选择物理课程和体育课程的学生id,在通过分组过滤筛选出同时选修两门课程的学生id
select student_id from score where score.course_id in (select cid from course where cname in ('物理','体育')) group by student_id having count(course_id)=2

# 3.通过学生id查询学生姓名,在通过not in找出没有同时选修物理课程和体育课程的学生姓名
select sname from student where student.sid not in (select student_id from score where score.course_id in (select cid from course where cname in ('物理','体育')) group by student_id having count(course_id)=2)  # count(course_id)=2  同时选修两门课程

习题1:查询所有的课程的名称以及对应的任课老师姓名

# 查询所有的课程的名称以及对应的任课老师姓名
# 1.用到course、teacher表
select teacher.tname,course.cname from course inner join teacher on course.teacher_id=teacher.tid;

# 2.美化代码
SELECT
	teacher.tname,
	course.cname 
FROM
	course
	INNER JOIN teacher ON course.teacher_id = teacher.tid;

习题2:查询学生表中男女生各有多少人

# 查询学生表中男女生各有多少人
# 1.用到的表student
select gender as '性别',count(gender) as '人数' from student group by gender

# 2.美化代码
SELECT
	gender AS '性别',
	count( gender ) AS '人数' 
FROM
	student 
GROUP BY
	gender;

习题3:查询物理成绩等于100的学生的姓名

# 查询物理成绩等于100的学生的姓名
# 1.用到的表course、score、student
select cid from course where cid = '2'

# 2.查询score选修了物理的学生id
select student_id from score where course_id in (select cid from course where cid = '2') and num=100

# 3.去student表中查询学生姓名
select sname from student where sid in (select student_id from score where course_id in (select cid from course where cid = '2') and num=100)

习题4:查询平均成绩大于八十分的同学的姓名和平均成绩

4、查询平均成绩大于八十分的同学的姓名和平均成绩
	# 1.本题需要用到score、student两张表
	# 2.解题思路:先查询平均成绩大于八十分的同学id,然后在连表
	步骤1: 按照学生id分组,之后求每个学生的平均成绩
		select student_id,avg(num) as avg_num from score group by student_id;
		
	步骤2:在使用having过滤出平均成绩大于80分的学生id  # 给聚合函数起一个别名,要不然多表查询拿不到数据
		select student_id,avg(num) as avg_num from score group by student_id having avg(num)>80;
		
	步骤3:用子列表把查询到的表跟student表,按照学生id拼接到一起
		select student.sname,m1.avg_num from student inner join (select student_id,avg(num) as avg_num from score group by student_id having avg(num)>80) as m1 on student.sid=m1.student_id;
		
步骤4:美化SQL语句
SELECT
	student.sname,
	m1.avg_num 
FROM
	student
	INNER JOIN ( SELECT student_id, avg( num ) AS avg_num FROM score GROUP BY student_id HAVING avg( num )> 80 ) AS m1 ON student.sid = m1.student_id;

习题5:查询所有学生的学号,姓名,选课数,总成绩

# 查询所有学生的学号,姓名,选课数,总成绩
# 用到的表student、score、course


# 1.找到学号、选修课数、总成绩
select student_id,count(course_id), sum(num) from score group by score.student_id;

# 2.找到学生姓名
select student.sid,student.sname from student;

# 3.找到所有信息
select sid,sname,m1.course_id,m1.sum_num from student inner join (select student_id,count(course_id) as course_id, sum(num) as sum_num from score group by score.student_id ) as m1 on student.sid=m1.student_id;

优化:
SELECT
	sid,
	sname,
	m1.course_id,
	m1.sum_num 
FROM
	student
	INNER JOIN ( SELECT student_id, count( course_id ) AS course_id, sum( num ) AS sum_num FROM score GROUP BY score.student_id ) AS m1 ON student.sid = m1.student_id;