硅基计划5.0 MySQL 叁 E-R关系图联合/多表查询三大连接子查询合并查询
文章目录
- 一、E-R关系图
- 1. 实体之间的关系
- 2. 简单的E-R图绘制
- 3. 工作流程
- 4. 外键约束&E-R图完成数据库/表创建
- 1. 一对一关系
- 2. 一对多关系
- 3. 多对多关系
- 二、联合/多表查询
- 1. 笛卡尔积
- 2.内连接
- 3. 外连接
- 4. 自连接
- 5. 子查询
- 6. 合并查询
一、E-R关系图
1. 实体之间的关系
一个实体类对应的是数据库的一张表,实体之间的关系说白了就是表和表之间的关系
一共有四种关系一对一,一对多,多对多,无关系
- 一对一:一名学生只能有一个学生账号,并且一个学生账号只能属于一名学生
- 一对多:一名学生属于一个班级,但是一个班级可以包含多名学生
- 多对多:一名学生可以选择对多门课程,同时一门课程也可以被多名学生选择
2. 简单的E-R图绘制
这里我把刚刚的例子具体展现出来
请注意,我们以后在工作中很少直接去绘制E-R图,因为这样非常耗费时间
3. 工作流程
对需求可行性分析-->
总体设计(E-R)图-->
具体数据库数据表设计-->
编写代码
4. 外键约束&E-R图完成数据库/表创建
我们针对不同的关系,讲讲如何设计
1. 一对一关系
就跟我们之前讲的一样,我们引入外键约束,而且我们要遵循三大范式,即一个表只能有一个主键
我们只需要在一张表中有一个列,和另外一个表建立联系就好
2. 一对多关系
我们针对这种关系,我们在“多”的那一方进行对“一”的联系
我们回到刚刚之前那个学生和班级表
对于学生和班级来说,班级是“一”,学生是“多”
我们就按照上述图片在学生表中加入一列和班级表关联就好
3. 多对多关系
这个非常特殊,我们可以建立一个中间表,来构成一个桥梁
就拿我们之前的那个学生和课程之间的关系来说
我们有个学生表
create table student (student_id int primary key,name varchar(50) not null,major varchar(50)
);
我们有个课程表
create table course (course_id int primary key,course_name varchar(100) not null,credits int
);
我们写个中间关系表,构建关系
对于学号和课程好之间构成复合主键,再分别与学生表和课程表构建外键联系
create table student_course (student_id int,course_id int,enrollment_date date,grade decimal(4,2),primary key (student_id, course_id), -- 复合主键,关键foreign key (student_id) references student(student_id),foreign key (course_id) references course(course_id)
);
关于E-R关系图,MySQL的workbench可以自动生成,可以自己去查阅下
这里给出一个题目和一个例子:
题目:学校食堂管理系统,包含食堂表,食堂仓口表,仓口收费记录表
create table type(house_id int primary key auto_increment,name varchar(10));create table access(access_id int primary key auto_increment,name varchar(10),house_id int,foreign key (house_id) references type(house_id));create table money(sign_id int primary key auto_increment,access_id int,house_id int,time datetime,foreign key (access_id) references access(access_id),foreign key (house_id) references type(house_id));
我们利用第二张表和第一张表之间的外键关系,以及第三张表和第二与第一张表的外键关系
来共同构成多个一对多关系的设计
二、联合/多表查询
我们前提是要保证查询的多个表之间要有关系,如果没有关系,那笛卡尔积没有意义
1. 笛卡尔积
我们首先来说说什么事笛卡尔积,说白了就是把所有可能的结果都枚举出来
举个例子
好,通过观察这个表,我们发现有两条是不合理数据,即张三怎么是二班的还有李四怎么是一班的
这就是笛卡尔积的计算结果,我们要自己手动取出不合理结果,即我们要保证两个班级id要统一
这个class_id
就是连接两张表之间的桥梁,我们就称为连接条件
好,我们来演示去重
create table class1(class_id int primary key auto_increment,class_name varchar(3));insert into class1 values(1,'一班'),(2,'二班');create table student1(student_id int,name varchar(10),class_id int,foreign key student1(class_id) references class1(class_id));insert into student1 values(1001,'张三',1),(1002,'李四',2);select * from class1,student1;select * from class1,student1 where class1.class_id = student1.class_id;
之后的操作和单表查询一样,都可以利用之前学的查询语句去增删改查等等
2.内连接
这个内连接说白了就是我们之前笛卡尔积的同义替换,可以相互转化
语法 ... (inner) join ... on (条件)
像我们之前的笛卡尔积计算,我们可以这么写
比如我们联合查询学生表和班级表
select * from student inner join class;
引入连接条件去重
select * from student inner join class on student.class_id = class.class_id;
再针对列进行筛选
select * from student inner join class on student.class_id = class.class_id where student.name = '张三';
我们可以看到当使用join on
语句后,并不影响我们where
语句的使用
那既然两者都一样,那我们为什么还要使用jion on
呢?
这是因为我们在join on
语句中能够拥有很多条件,更加利于我们查询
如果想让多个表连接,比如以下多对多关系的表,我们可以搭配多组join on
语句
select * from join score on student.student_id = score.student_id join course on score.course_id = course.course_id;
3. 外连接
这个不能通过where
语句写,只能通过join on
方式写,以下我举个例子来演示下
这个外连接分位左外连接和右外连接
首先我们拿一对一关系的表来演示下
use home;show tables;create table student2(id int,name varchar(20));create table score(student_id int,score int);insert into student2 values(1,'张三'),(2,'李四'),(3,'王五');insert into score values(1,90),(2,80),(3,70);
当我们使用内连接和外连接的时候,结果是一样的
但是如果不是一对一的关系,就会存在差别
比如我们把成绩表中70分的成绩的学号修改为4
update score set student_id = 4 where score = 70;
好,现在我们再来看左外连接和右外连接的结果
外连接本质上就是笛卡尔积
左外连接,因为学号为三的王五在成绩表中不存在,因此三班王五并没有直接参与笛卡尔积运算,而是把另一张表中不存在的数据标记为空
同理右外连接,因为学号四在学生表中不存在,因此学号四号也并没有直接参与笛卡尔积,同理而是把另一张表中不存在的数据标记为空
但是总得来说,我们平时还是要避免这种几张表中数据不对等的情况
4. 自连接
我们之前都是列与列之间的比较,比如语文成绩大于数学成绩
但是如果是这样的,每一行表示一门成绩
我们之前由于都是一行代表一个学生所有成绩
但是现在成绩是一条一条插入的,这样我们如何比较呢,比如我们想查看MySQL成绩大于数据结构成绩的学生信息
因此现在我们就要进行内连接查询,但是如果你直接写内连接,会报错
提示列名重复,怎么办呢,诶!我们可以起别名啊,这样不就行了,同时再把去重条件加上
观察这张表,我们要比较MySQL成绩大于数据结构成绩,那我们不是直接可以比较条件就是
s1.class_id = 1 and s2.class_id = 2 and s1.score > s2.score
因此我们加上限制语句写
但是如果我们并不知道课程号的对应关系,我们还可以联合其他表进行多表查询
但是,在实际开发环境中,我们并不建议使用诸如笛卡尔积这种查询方法,如果表的数据非常庞大,这样的笛卡尔积算起来费时费力
因此在以后的开发环境中,我们使用诸如Java等其他语言对MySQL进行查询操作
5. 子查询
这个子查询其实是和我们的SQL常识相反的存在
子查询说白了就是把一个SQL的查询结果当成另一个SQL的一部分,它可以是另一个SQL语句的条件。同时,因为查询的结果是一个表,因此它还可以和其他表进行笛卡尔积
我们就拿以下数据来举例子
我们想查询和张三同班的同学信息
如果按照一般方式,我们要分两步走
第一步是先查询张三位于哪个班级,第二步再是筛选出这个班级的其他同学同时不包含张三本人
select class_id from student3 where name = '张三';select * from student3 where class_id = 1;
如果我们使用子查询,就是这样
select * from student3 where class_id = (select class_id from student3 where name = '张三') and name != '张三';
如果是多个行,也是一样的写法,比如查找成绩表中张三和李四的成绩
select * from course3 where student_id in (select id from student3 where name = '张三' or name = '李四');
对于多个列的子查询,或者在from
子句中的查询,这里就不再赘述,就是和我们之前讲的单表或者是多表查询是一样的
6. 合并查询
这个和刚刚的子查询类似,但是这种查询写法客服行更高
合并查询就是把多个SWL查询的结果合并为一个集合,关键字union
和union all
我们再创建一个新的表
如果我想查询student3
表中一班的同学和新的这张表中三班的同学
select * from student3 where class_id = 1 union select * from student4 where class_id = 3;# 使用union代表结果去重,union all代表结果不去重
这里说个注意事项,在进行合并查询的时候,要保证查询的多个表之间的列个数,列的类型要匹配,对于列名并无特殊要求