MySQL 表关系及多表操作(联合查询连接查询子查询)

Posted YuLong~W

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL 表关系及多表操作(联合查询连接查询子查询)相关的知识,希望对你有一定的参考价值。

表关系

表关系:一个表代表一个实体,实体之间都有关联关系的

  • 根据 范式的要求 来设计表关系,减少数据冗余
  • 根据 实际需求 来设计表关系,提升访问效率

1、表关系是体现数据实际联系的方式

2、表关系的设计好坏直接关联数据维护的准确性、有效性

3、良好的数据库设计有助于后期程序开发

  • 一对一关系
  • 一对多关系(多对一)
  • 多对多关系

一对一关系

一对一关系:一张表中的一条记录与另外一张表中有且仅有一条记录有关系

  • 一对一关系通常是用来将一张原本就是一体的表拆分成两张表
    • 频繁使用部分:常用字段
    • 不常使用部分:生僻字段
    • 使用相同的主键对应
  • 一对一关系设计较多使用在优化方面
  • 因为一对一关系表通常有相同信息作为匹配条件,所以查询方式也比较方便
    • 连表操作:利用共有信息进行匹配,一并查出一条完整信息
    • 多次查询:利用共有信息进行多表查询,利用程序组合成一条完整信息

步骤:

  • 一张表的数据字段较多且数据量较大
  • 表中有部分字段使用频次较高,而另一部分较少使用
  • 将常用字段和不常用字段拆分成两张表,使用同样的主键对应

示例

1、学生信息表

学号(P)姓名性别年龄身高体重籍贯政治面貌
1张飞20178160农民
2武则天21168110党员
  • 以上数据表信息字段较多
  • 姓名、性别、年龄属于常用字段,频繁查询

2、一对一关系设计

  • 将常用字段取出,与学号组合成一张常用表
  • 将不常用字段取出,与学号组合成一张不常用表
  • 表与表数据对应关系:基于学号(唯一)是一对一关系

常用表

学号(P)姓名性别年龄
1张飞20
2武则天21

不常用表

学号(P)身高体重籍贯政治面貌
1178160农民
2168110党员

一对多关系

一对多关系:也叫多对一关系,一张表中的一条记录与另外一张表的多条记录对应,反过来另外一张表的多条记录只能对应当前表的一条记录

  • 一对多关系是实体中非常常见的一种关系,实体设计时也应用非常多
  • 一对多关系的核心解决方案是如何让记录能够正确匹配到另外表中的数据
    • 一表设计:一表记录在另外一张表中有多条记录,所以无法记录多个字段(违背1NF)
    • 多表设计:多表记录在另外一张表中只有一条记录,可以设置字段记录对应的主属性(通常主键)
  • 一对多关系设计是将实体的关系在表结构层进行强制关联(没有关系程序层也可以控制,但是会非常麻烦)
    • 便于连表操作
    • 便于数据分析统计(数据库层)
  • 一对多关系的核心在于分析出表与表之间的关系

步骤:

  • 确定实体间的关系为一对多(多对一)关系
  • 在多表中增加一个字段记录一表中对应的主属性

示例

1、老师与学科间的关系:一个老师只能教一个学科,但是一个学科有多个老师教授,学科与老师形成的关系就是一对多(反过来老师与学科的关系就是多对一关系)

老师表(多表)

老师ID(P)姓名年龄性别
1张老师35
2李老师34
3王老师30

学科表(一表)

学科ID(P)名字课时长度
1php600
2Java800
  • 以上两个实体没有体现彼此之间的关联关系
  • 实际上讲师与学科肯定是有关联的

2、在多表(讲师)中增加字段维护一表(学科)的关系型,形成多对一关系

老师ID(P)姓名年龄性别学科ID
1张三351
2李四341
3王五302
  • 基于新的讲师表与学科表产生了关联关系(多对一)
  • 基于讲师表可以知道讲师所属学科
  • 基于学科ID可以统计出不同学科的讲师数量

多对多关系

多对多关系:一张表中的一条记录对应另外一个表中多条记录,反过来一样

  • 多对多关系在实体中是最常见的关系
  • 多对多关系无法在自身表中维护对应表关系(违背1NF),需要通过第三方表来实现将多对多关系变成多个多对一关系
    • 设计一个中间表:记录两张表之间的对应关系(主属性)
    • 中间表与其他表都是多对一的关系
  • 多对多关系是否建立主要看业务上是否存在数据要求,如果不存在数据需求,那么就没必要刻意设计

步骤:

  • 确定实体间的关系为多对多关系
  • 设计中间表,记录两张表的对应关系

示例

1、老师与学生之间的关系:一个老师会教授多个学生,一个学生也会听多个老师的课,所以实体关系是多对多关系

老师表

老师ID(P)姓名年龄性别
1张老师35
2李老师34
3王老师30

学生表

学生ID(P)姓名年龄性别
1小明15
2小红14
3小萌14
  • 以上实体没有从结构上体现表之间的关系

2、设计一个中间表:老师与学生关系表,将老师与学生的对应关系对应上(多对一)

中间表

ID(P)学生ID老师ID
111
212
313
421
522
623
731
833
  • 中间表与老师表的对应关系是多对一:通过老师ID可以找到每一个上过课的老师
  • 中间表与学生表的对应关系是多对一:通过学生ID可以找到每一个听过课的学生
  • 老师找学生:老师表–>中间表(找出老师对应的学生ID)–>学生表(找出学生ID对应的学生信息)
  • 学生找老师:学生表–>中间表(找出学生对应的老师ID)–>老师表(找出老师ID对应的老师信息)

总结

1、表关系的设计是要遵循范式规范作为前提

2、表关系的设计是根据实体关系以及业务需求进行设计

  • 一对一关系:主要在于优化访问效率、传输效率
  • 一对多关系:在于如何让实体间的联系在结构中体现(后期可以使用外键进行相关约束保证数据的有效性)
  • 多对多关系:与一对多关系一样,清晰明了的体现实体间的结构联系

3、在设计数据库的时候,要严格使用表关系来进行实体关联设计

  • 基于表关系来实现实体间的关联控制
  • 在设计和应用表的时候提炼对表关系的认知
  • 能够熟练的基于业务控制数据库的关系

mysql多表操作

不管是大型还是小型项目,一个数据库里都会有N张表,表之间也通过一对一、多对一或者多对多关系进行关联

  • 多表操作是实际开发时经常遇到的解决问题的方案
  • 多表操作能够在数据库层就实现大量数据的组合或者筛选

联合查询

联合查询union,是指将多个查询结果合并成一个结果显示

  • 联合查询是针对查询结果的合并(多条select语句合并
  • 联合查询语法: select 查询【决定字段名字】union 查询选项 select 查询...
  • 联合查询要求:联合查询是结果联合显示
    • 多个联合查询的字段结果数量一致
    • 联合查询的字段来源于第一个查询语句的字段
  • 查询选项:
    • all:保留所有记录
    • distinct:保留去重记录 (默认)

1、创建一个表与t_1,并插入数据

create table t_2 like t_1;

insert into t_2 values(null,'犬夜叉','男',200,'神妖1班'),
(null,'日暮戈薇','女',16,'现代1班'),
(null,'桔梗','女',88,'法师1班'),
(null,'弥勒','男',28,'法师2班'),
(null,'珊瑚','女',20,'法师2班'),
(null,'七宝','保密',5,'宠物1班'),
(null,'杀生丸','男',220,'神妖1班'),
(null,'铃','女',4,'现代1班'),
(null,'钢牙','男',68,'神妖1班'),
(null,'奈落','男',255,'神妖1班'),
(null,'神乐','女',15,'神妖2班');
  • t_2与t_1结构一样,可以理解为因为数据量大拆分到了两个表中

2、使用联合查询将两张表的数据拼接到一起显示

select * from t_1
union
select * from t_2;

3、联合查询选项默认是distinct

select * from t_1
union 
select * from t_1;

select * from t_1
union all
select * from t_1;

4、联合查询不要求字段类型一致,只对数量要求一致,而且字段与第一条查询语句相关

select name from t_1
union all
select age from t_1;
  • 注意:如果数据不能对应,那么查询没有意义

5、如果使用where对数据进行筛选,where针对的是select指令,而不是针对union结果

select * from t_1
union all
select * from t_2
where gender = '女';
  • where只对第二条select有效
  • 若要全部有效,需要select都使用where

联合查询排序

联合查询排序:针对联合查询的结果进行排序

  • order by本身是对内存结果进行排序,union的优先级高于order by,所以order by 默认是对union结果进行排序
  • 联合排序需要区分排序的内容是 select结果 还是 union结果
    • union结果:在最后使用排序即可
    • select结果:需要针对select使用排序
      • ① select必须使用括号包裹(括号里使用order by)
      • ② select里的排序,order by必须配合limit才能生效(limit一个足够大的数值即可)

1、将t_1和t_2表的结果使用年龄降序排序

select * from t_1
union all
select * from t_2
order by age desc; #针对的是整个union之后的结果

2、t_1表按年龄降序排序,t_2表按年龄升序排序

# 无效方式
(select * from t_1 order by age desc)
union 
(select * from t_2 order by age);

# 正确方式
(select * from t_1 order by age desc limit 99999)
union 
(select * from t_2 order by age desc limit 99999);

连接查询

连接查询join,将两张表依据某个条件进行数据拼接

  • join左右各一张表:join关键字左边的表叫左表,右边的表叫右表
  • 连接查询的结果都是记录会保留左右表的所有字段(字段拼接),使得字段数量增加
    • 具体字段数据依据查询需求确定
    • 表字段冲突需要使用表别名字段别名区分
  • 不同的连表有不同的连接方式,对于结果的处理也不尽相同
  • 连接查询不限定表的数量,可以进行多表连接,只是表的连接需要一个一个的连 (A join B join C …)
  • 连接查询的目的是:将分散在不同表的数据组合到一起,方便外部使用数据

交叉连接

交叉连接cross join,不需要连接条件的连接

  • 交叉连接产生的结果就是笛卡尔积
    • 左表的每一条记录都会与右表的所有记录连接并保留
  • 交叉连接没有实际数据价值,只是丰富了连接查询的完整性,尽量避免出现
  • 交叉连接语法: 左表 cross join 右表

1、交叉连接t_1和t_2表

select * from t_1 cross join t_2; # t_1,t_2

内连接

内连接[inner] join,将两张表根据指定的条件连接起来,严格连接

  • 内连接是将一张表的每一条记录去另外一张表根据条件匹配
    • 匹配成功:保留连接的数据
    • 匹配失败:都不保留
  • 内连接语法: 左表 join 右表 on 连接条件
  • 内连接匹配规则就是:必须保证左表和右表同时存储连接关系,这样的数据才会保留
  • 可以没有on条件,那么得到的结果就是交叉连接(笛卡尔积),无意义
  • on关键字可以换成where,结果是一样(但是不建议使用)

1、设计学生表和专业表:学生对专业多对一关系

# 学生表
create table t_3(
	id int primary key auto_increment,
    name varchar(50) not null,
    course_no int
)charset utf8;
insert into t_3 values(null,'Student1',1),
(null,'Student2',1),
(null,'Student3',2),
(null,'Student4',3),
(null,'Student5',1),
(null,'Student6',default);

# 专业表
create table t_4(
	id int primary key auto_increment,
    name varchar(50) not null unique
)charset utf8;
insert into t_4 values(null,'Computer'),(null,'Software'),(null,'Network');

2、获取已经选择了专业的学生信息,包括所选专业

# 学生和专业在两个表中,所以需要连表
# 学生必须有专业,而专业也必须存在,所以是内连接
# 连接条件:专业编号
# 两张表有两个字段冲突:id、name,所以需要使用别名
select t_3.*,t_4.name as course_name from t_3 inner join t_4 on t_3.course_no = t_4.id;

# 表名的使用也可以使用别名
select s.*,c.name as c_name from t_3 as s inner join t_4 c on s.course_no = c.id;
  • 字段冲突的话在MySQL里倒是不影响,只是会同时存在,但是后续其他地方使用就不方便了

原理分析:

a.aid = b.id
继续下一个
a.aid <> b.id
继续下一个
连接开始
A inner join B on a.aid = b.id
连接条件循环匹配匹配
A表逐条取出记录与B表的每条记录匹配
保留匹配结果
根据字段要求保留
全部匹配完毕
放弃
取出所有匹配成功结果:结束

外连接

外连接outer join,是一种不严格的连接方式

  • 外连接分为两种
    • 左外连接(左连接):left join
    • 右外连接(右连接):right join
  • 外连接有主表和从表之分
    • 左连接:左表为主表
    • 右连接:右表为主表
  • 外连接语法: 左表 left/right join 右表 on 连接条件
  • 外连接是将主表的记录去匹配从表的记录
    • 匹配成功保留
    • 匹配失败(全表):也保留,只是从表字段置空
  • 外连接与内连接的区别在于数据匹配失败的时候,外连接会保留一条记录
    • 主表数据保留
    • 从表数据置空
  • 外连接必须使用on作为连接条件(不能没有或者使用where替代)

1、查出所有的学生信息,包括所在班级(左连接)

# 主要数据是学生,而且是全部学生:外连接、且学生表是主表
select s.*,c.name c_name from t_3 s left join t_4 c on s.course_no = c.id;

2、查出所有班级里的所有学生(右连接)

# 主表是班级
select s.*,c.name c_name from t_3 s right join t_4 c on s.course_no = c.id;

自然连接

自然连接natural join,是一种自动寻找连接条件的连接查询

  • 自然连接不是一种特殊的连接方式,而是一种自动匹配条件的连接。是基于内连接、外连接和交叉连接实现自动条件匹配而已
    • 没有条件(没有同名字段):交叉连接
    • 有条件:内连接/外连接(看关键字使用)
  • 自然连接包含自然内连接和自然外连接
    • 自然内连接:natural join
    • 自然外连接:natural left/right join
  • 自然连接条件匹配模式:自动寻找相同字段名作为连接条件(字段名相同)

1、自然连接t_3和t_4表

select  * from t_3 natural join t_4;

2、自然连接是不管字段是否有关系的,只管名字是否相同:如果想要自然连接成功,那么字段的设计就必须非常规范

create table t_5(
	s_id int primary key auto_increment,
    s_name varchar(50) not null,
    c_id int comment '课程id'
)charset utf8;
insert into t_5 select * from t_3;

create table t_6(
    c_id int primary key auto_increment,
    c_name varchar(50) not null unique
)charset utf8;
insert into t_6 select

以上是关于MySQL 表关系及多表操作(联合查询连接查询子查询)的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 基础 -- 多表关系(一对一1对多(多对一)多对多)多表查询(内连接外连接自连接子查询(嵌套查询)联合查询 union)笛卡儿积

MySQL主表与副表级联操作及多表查询(两张独立表,一张关系表)

MySQL主表与副表级联操作及多表查询(两张独立表,一张关系表)

MySQL DML操作--------多表联合查询实战

MySQL数据库多表查询

SQL多表联合查询