1、join多表连接查询
案例准备
use schoolstudent: 学生表sno: 学号sname: 学生姓名sage: 学生年龄ssex: 学生性别teacher: 教师表tno: 教师编号tname: 教师名字course: 课程表cno: 课程编号cname: 课程名字tno: 教师编号sc: 成绩表sno: 学号cno: 课程编号score: 成绩# 项目构建drop database school;CREATE DATABASE school CHARSET utf8;USE schoolCREATE TABLE student(sno INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '学号',sname VARCHAR(20) NOT NULL COMMENT '姓名',sage TINYINT UNSIGNED NOT NULL COMMENT '年龄',ssex ENUM('f','m') NOT NULL DEFAULT 'm' COMMENT '性别')ENGINE=INNODB CHARSET=utf8;CREATE TABLE course(cno INT NOT NULL PRIMARY KEY COMMENT '课程编号',cname VARCHAR(20) NOT NULL COMMENT '课程名字',tno INT NOT NULL COMMENT '教师编号')ENGINE=INNODB CHARSET utf8;CREATE TABLE sc (sno INT NOT NULL COMMENT '学号',cno INT NOT NULL COMMENT '课程编号',score INT NOT NULL DEFAULT 0 COMMENT '成绩')ENGINE=INNODB CHARSET=utf8;CREATE TABLE teacher(tno INT NOT NULL PRIMARY KEY COMMENT '教师编号',tname VARCHAR(20) NOT NULL COMMENT '教师名字')ENGINE=INNODB CHARSET utf8;INSERT INTO student(sno,sname,sage,ssex)VALUES (1,'zhang3',18,'m');INSERT INTO student(sno,sname,sage,ssex)VALUES(2,'zhang4',18,'m'),(3,'li4',18,'m'),(4,'wang5',19,'f');INSERT INTO studentVALUES(5,'zh4',18,'m'),(6,'zhao4',18,'m'),(7,'ma6',19,'f');INSERT INTO student(sname,sage,ssex)VALUES('oldboy',20,'m'),('oldgirl',20,'f'),('oldp',25,'m');INSERT INTO teacher(tno,tname) VALUES(101,'oldboy'),(102,'hesw'),(103,'oldguo');DESC course;INSERT INTO course(cno,cname,tno)VALUES(1001,'linux',101),(1002,'python',102),(1003,'mysql',103);DESC sc;INSERT INTO sc(sno,cno,score)VALUES(1,1001,80),(1,1002,59),(2,1002,90),(2,1003,100),(3,1001,99),(3,1003,40),(4,1001,79),(4,1002,61),(4,1003,99),(5,1003,40),(6,1001,89),(6,1003,77),(7,1001,67),(7,1003,82),(8,1001,70),(9,1003,80),(10,1003,96);SELECT * FROM student;SELECT * FROM teacher;SELECT * FROM course;SELECT * FROM sc;
1.1、笛卡尔乘积
SELECT * FROM teacher , course;
或者:
SELECT * FROM teacher JOIN course;
或者:
SELECT * FROM teacher INNER JOIN course;
拿着 teacher表每行数据和 course表逐行进行组合, 显示两层for循环的实现逻辑, Simple-Nextloop (嵌套循环方式)得出的结果, 会有部分数据是无意义的
注意: 生产禁用这种方式, 会极大增加 mysql压力
1.2、内连接
参考链接
https://www.runoob.com/mysql/mysql-join.html
#内连接是取两会张表拼接, 有共同值的部分
SELECT * FROM teacher JOIN course ON teacher.tno=course.tno;
或者
SELECT * FROM teacher INNER JOIN course ON teacher.tno=course.tno;
#on 相当于 where条件过滤
+-----+--------+------+--------+-----+
| tno | tname | cno | cname | tno |
+-----+--------+------+--------+-----+
| 101 | oldboy | 1001 | linux | 101 |
| 102 | hesw | 1002 | python | 102 |
| 103 | oldguo | 1003 | mysql | 103 |
+-----+--------+------+--------+-----+
1.3、外连接
1.3.1、左外连接
#left join: 获取左表所有记录, 过滤右表没有对应匹配的记录
SELECT * FROM teacher LEFT JOIN course ON teacher.tno=course.tno;
+-----+--------+------+--------+------+
| tno | tname | cno | cname | tno |
+-----+--------+------+--------+------+
| 101 | oldboy | 1001 | linux | 101 |
| 102 | hesw | 1002 | python | 102 |
| 103 | oldguo | 1003 | mysql | 103 |
| 104 | oldx | NULL | NULL | NULL |
| 105 | oldw | NULL | NULL | NULL |
+-----+--------+------+--------+------+
1.3.2、右外连接
#right join: 与left join相反, 获取右表所有记录, 过滤左表没有对应匹配的记录
SELECT * FROM teacher RIGHT JOIN course ON teacher.tno=course.tno;
+------+--------+------+--------+-----+
| tno | tname | cno | cname | tno |
+------+--------+------+--------+-----+
| 101 | oldboy | 1001 | linux | 101 |
| 102 | hesw | 1002 | python | 102 |
| 103 | oldguo | 1003 | mysql | 103 |
| NULL | NULL | 1004 | k8s | 108 |
+------+--------+------+--------+-----+
1.3.3、导出连接查询的结果集
#目录需要有 mysql用户权限, 指定分隔符为(,) 导出csv文件
vim my.cnf
secure_file_priv=""
SELECT * FROM teacher RIGHT JOIN course ON teacher.tno=course.tno into outfile '/data/join.csv' fields terminated by ',';
1.3.4、连接查询性能影响
连接查询相当于 两层 for循环, 合成一张表, on 条件过滤 相当于 if 取条件判断
对于大表来说会极其影响性能
优化方案: 给两张表关联的字段添加索引
1.4、多表连接语法
1.4.1、a和b有直接关联关系
#a表的z 和 b表的z有关联字段
select a.x,b.y
from a
join b on a.z=b.z
where
group by
having
order by
limit;
1.4.2、a和b没有直接关联关系
假如: a 和 c 有关, b 和 c 有关
a join c on a.i = c.i join b on c.n=b.n
方法:
1. 根据题意将所有涉及到的表找出来 a b
2. 找到a和b直接或者间接的关联条件
3. 用join on 语句把所有表连接到一起 (连接后当成一张表操作)
4. 罗列其他查询条件
注意:
MySQL中建议连接的表不要超过4个, 否则需要考虑重新设计表
相当于多一个表多一层 for循环
1.5、练习
1.5.1、查询每位老师所教课程名称
SELECT teacher.tname,course.cname
FROM teacher JOIN course
ON teacher.tno=course.tno;
步骤分析:
1. 找出需要查询的表和关联条件
FROM teacher JOIN course
ON teacher.tno=course.tno;
2. 找出需要查询的内容, 老师 和 课程名
SELECT teacher.tname,course.cname
FROM teacher JOIN course
ON teacher.tno=course.tno;
1.5.2、统计每个学员, 学习课程的门数
1. 分析: 需要的数据再student表、sc表,course表, 但是sc表记录了课程名, 使用 student表和 course表连接
SELECT *
FROM student
INNER JOIN sc
ON student.sno=sc.sno;
2. 根据 sno进行分组
SELECT student.sname, count(*)
FROM student
INNER JOIN sc
ON student.sno=sc.sno
GROUP BY student.sno;
3. 优化: 增加姓名和学号显示
SELECT concat("姓名",student.sname,"学号:",student.sno), count(*)
FROM student
INNER JOIN sc
ON student.sno=sc.sno
GROUP BY student.sno;
