四、MySQL多表操作

2020-03-10  本文已影响0人  Ly3911

示例

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

作者表:id、用户名、密码

新闻表:id、标题、内容、发布时间、作者id

显示新闻的时候是肯定需要显示作者姓名的

小结

1、多表操作是实际开发时经常遇到的解决问题的方案

2、多表操作能够在数据库层就实现大量数据的组合或者筛选

一、联合查询

1、联合查询

概念

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

select 查询【决定字段名字】
    union 查询选项
select 查询
...

步骤

1、确定要进行多个表数据的联合操作

2、确定数据的要求:全部保留 or 去重

3、使用联合查询

示例

1、创建一个表与t_40结构相同,并插入数据

create table t_42 like t_40;

insert into t_42 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班');

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

select * from t_40 
union
select * from t_42;

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

select * from t_40
union 
select * from t_40;

select * from t_40
union all
select * from t_40;

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

select name from t_40
union all
select age from t_40;

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

select * from t_40 
union all
select * from t_42
where gender = '女';

小结

1、union是负责将多次查询的结果统一拼凑显示

2、union常用方式

3、union默认是去重的,想要保留全部查询结果,需要使用union all

2、联合查询排序

概念

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

步骤

1、确定需要对联合查询进行排序

2、确定排序内容

3、选择合适的排序方式

示例

1、将t_40和t_42表的结果使用年龄降序排序

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

2、t_40表按年龄降序排序,t_42表按年龄升序排序

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

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

小结

1、联合排序需要区分排序的内容是select结果还是union结果

二、连接查询

概念

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

小结

1、连接查询就是通过字段拼接,把两张表的记录变成一条记录:字段数量增加

2、连接查询的目的是将分散在不同表的数据组合到一起,方便外部使用数据

1、交叉连接

概念

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

示例

交叉连接t_41和t_42表

select * from t_41 cross join t_42; # t_41,t_42

小结

1、笛卡尔积无意义,尽量避免出现

2、内连接

概念

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

步骤

1、确定需要从多张表中获取数据组成记录

2、确定连接的要求是保留连接成功的,不成功的数据不要

3、使用内连接

示例

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

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

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

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

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

# 表名的使用也可以使用别名
select s.*,c.name as c_name from t_43 as s inner join t_44 c on s.course_no = c.id;

原理分析

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

小结

1、内连接匹配规则就是必须保证左表和右表同时存储连接关系,这样的数据才会保留

2、扩展:内连接可以没有on条件,那么得到的结果就是交叉连接(笛卡尔积),无意义

3、扩展:内连接的on关键字可以换成where,结果是一样(但是不建议使用)

3、外连接

概念

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

步骤

1、确定进行连表操作

2、确定要有数据保护,即表中数据匹配失败也要保留

3、确定主从表

4、选择对应外连接

示例

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

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

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

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

小结

1、外连接与内连接的区别在于数据匹配失败的时候,外连接会保留一条记录

2、外连接不论是左连接还是右连接,字段的顺序不影响,都是先显示左表数据,后显示右表数据

3、外连接必须使用on作为连接条件(不能没有或者使用where替代)

4、自然连接

概念

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

步骤

1、需要进行连表查询结果

2、连表查询的表字段能够直接关联(字段名字相同:非常高的表结构设计)

3、选择合适的连接方式:内连接 or 外连接

4、使用自然连接

示例

1、自然连接t_43和t_44表

select  * from t_43 natural join t_44;

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

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

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

# 自然连接:条件只有一个相同的c_id
select * from t_45 natural join t_46;

小结

1、自然连接本身不是一种特别连接,是基于内连接、外连接和交叉连接实现自动条件匹配而已

2、自然连接使用较少,因为一般情况下表的设计很难做到完全标准或者不会出现无关同名字段

5、using关键字

概念

using关键字:连接查询时如果是同名字段作为连接条件,using可以代替on出现(比on更好)

步骤

1、需要进行连表进行数据查询

2、两个表的连接条件字段同名

3、使用using关键字作为连接条件

示例

查询t_45中所有的学生信息,包括所在班级名字

select s.*,c.c_name from t_45 s left join t_46 c using(c_id);
select * from t_45 s left join t_46 c using(c_id);

小结

1、using关键字用来简化同名条件字段的连接条件行为

2、using关键字与自然连接相似,但是比自然连接灵活,可以指定有效的同名连接条件,忽略无效的同名字段

6、总结

1、连接查询是实际开发过程中应用最多的查询方式

2、连接查询的效率肯定没有单表查询高

3、连接查询中使用的较多的就是内连接和外连接

三、子查询

概念

子查询:sub query,通过select查询结果当做另外一条select查询的条件或者数据源

示例

想查出某个专业的所有学生信息

按照以前的知识,可以产生两种解决方案:

1、分开查询

select c_id from t_46 where c_name = '专业名字';
select * from t_45 where c_id = '查出来的专业id';

2、连表查询

select s.* from t_45 s right join t_46 c using(c_id) where c.c_name = '专业名字';

从解决方案分析

1、分开查询数据量小,但是麻烦

2、连接查询方便,但是效率不高(先连后筛选)
如果能够将方案1变成一个简单的方式就好了

select * from t_45 where c_id = (select c_id from t_46 where c_name = '专业名字');

以上就是子查询

小结

1、子查询就是能够将一些具有先后顺序的查询组装到一个查询语句中,从而节省操作的过程,降低复杂程度

1、子查询分类

概念
子查询分类:根据子查询出现的位置或者产生的数据效果分类

小结

1、通常我们使用子查询结果定义分类

2、位置划分是包含子查询结果的

2、标量子查询

概念
标量子查询:子查询返回的结果是一行一列,一个值

步骤

1、确定要从一张表中获取数据(可以是多张)

2、确定查询条件在当前查询表中无法实现但是可以从其他表中精确获得(只有一个)

3、使用标量子查询

示例

获取Computer专业的所有学生

# 数据目标:学生表t_45
# 条件:专业名字,不在t_45中,但是t_45中的专业id可以通过专业名字在另外一张表精确获得(一个值)

select * from t_45 where c_id = (select c_id from t_46 where c_name = 'Computer');

小结

1、标量子查询通常用简单比较符号来制作条件的

3、列子查询

概念
列子查询:子查询返回的结果是一列多行

步骤

1、确定要从一张表中获取数据(可以是多张)

2、确定查询条件在当前查询表中无法实现但是可以从其他表中精确获得(一个字段多个数据)
3、使用列子查询

示例

1、获取所有有学生的班级信息

# 数据获取目标是班级信息
# 数据获取条件是在学生表中的班级id,是多个

select * from t_46 where c_id in (select distinct c_id from t_45 where c_id is not null);

小结

1、列子查询通常是作为外部主查询的条件,而且是使用in来进行判定

4、行子查询

概念
行子查询:子查询返回的结果是一行多列

步骤
1、确定获取数据的条件不只是一个字段

2、确定数据条件的来源不在当前表中(也可以在当前表),但是可以通过条件精确获取到(一行多列)

3、使用行子查询

示例

获取学生表中性别和年龄都与弥勒相同的学生信息

# 查询条件有多个:性别和年龄
# 数据的条件的来源在另外一张表中

# 解决思路:两个标量子查询
select * from t_40 where gender = (select gender from t_42 where name = '弥勒') and age = (select age from t_42 where name = '弥勒');

问题分析:以上查询解决了问题但是用到了两次子查询(效率降低),而且查询语句是一样的,只是字段不一样,可以使用行子查询解决

# 构建条件行元素(gender,age)

select * from t_40 where (gender,age) = (select gender,age from t_42 where name = '弥勒');

小结

1、行子查询是可以使用多个标量子查询替代解决问题的,但是行子查询的效率会比多个标量要高。需要使用到行子查询的时候不会使用标量子查询来解决的

5、表子查询

概念

表子查询:子查询返回的结果是多行多列(二维表)

步骤

1、需要查询的数据通过一次SQL查询不能直接搞定(可能顺序关系导致)

2、如果先把结果加工后(多行多列),外部再来一层结果查询加工可以完成目标

3、使用表子查询

示例

获取学生表中每个班级里年龄最大的学生信息(姓名、年龄、班级名字),然后按年龄降序排序显示

# 尝试直接解决
select any_value(name),max(age) m_age,class_name from t_42 group by class_name order by m_age desc;

解决方案:要是在分组之前将所有班级里的学生本身是降序排序,那么分组的第一条数据就是满足条件的数据。但是问题是:order by必须出现在 group by之后,如何解决?

# order by必须在group by之前解决:就要想办法让order by在group by之前而且不在同一条select指令中(同一条无解)
# 必须使用子查询解决在不用SQL中的问题,而子查询的结果应该是全部记录信息,所以应该是表子查询,而且是数据源

select any_value(name),max(age),class_name from 
(select name,age,class_name from t_42 order by age desc) as t
group by class_name;
select any_value(name),max(age),class_name from 
(select name,age,class_name from t_42 order by age desc limit 99999) as t
group by class_name;
select any_value(name),max(age) m_age,class_name from 
(select name,age,class_name from t_42 order by age desc limit 99999) as t
group by class_name order by m_age;

小结

1、表子查询通常解决的问题是提供数据源

2、表子查询出现的业务

3、特别注意:在MySQL7以后,子查询中使用的order by子句需要配合limit才会生效

6、exists子查询

概念
exists子查询:代入查询,将主表(外部查询)的每一行代入到子表(子查询表)进行校验

步骤

1、确定查询的数据来自主表

2、确定条件是需要去子表(其他表)进行验证:不需要去子表获取数据之类的

3、使用exists子查询

示例
获取所有有学生的班级信息t_46

# 获取的数据是班级表t_46
# 班级是否有学生需要在t_45中确认,并不需要t_45提供任何数据显示

select * from t_46 c where exists(select c_id from t_45 where c.c_id = c_id);

小结

1、exists子查询通常用来解决那种不需要数据但是需要去表中确认关系的查询问题

7、比较方式

概念
比较方式:在子查询中可以使用一些特定的比较方式

示例

1、找出t_40表中与t_42表中年龄相同的信息

# 数据获取在t_40表
# 数据条件在t_42表

# 解决方案1:使用in列子查询
select * from t_40 where age in (select distinct age from t_42);

# 解决方案2:使用exists子查询
select * from t_40 t1 where exists(select id from t_42 where t1.age = age);

# 解决方案3:使用any或者some匹配(列子查询)
select * from t_40 where age = some(select age from t_42);

小结
1、比较方式其实很多都可以实现替代,越精准的数据匹配方式效率就越高

8、总结

1、子查询通常使用较多的是标量子查询、列子查询和exists子查询

2、子查询的效率是比连接查询的效率要低的,要适当选择使用

3、理论上来讲,不限制子查询的嵌套,但是考虑到效率的降低,不建议使用子查询嵌套

上一篇 下一篇

猜你喜欢

热点阅读