联表查询JOIN介绍

大家好,因为最近项目里面涉及多张表,会使用到很多联表查询,所以今天给大家分享一下联表查询里面的JOIN的相关知识。

 

一、背景介绍

MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。

关系型数据库三范式:

第一范式(1NF):字段值具有原子性,不能再分(所有关系型数据库系统都满足第一范式);

例如:姓名字段,其中姓和名是一个整体,如果区分姓和名那么必须设立两个独立字段;

第二范式(2NF):一个表必须有主键,即每行数据都能被唯一的区分;

备注:必须先满足第一范式;主键可以将表格变为一个聚集索引

第三范式(3NF):一个表中不能包涵其他相关表中非关键字段的信息,即数据表不能有沉余字段;

备注:必须先满足第二范式;往往我们在设计表中不能遵守第三范式,因为合理的沉余字段将会给我们减少join的查询;典型的以空间换时间思想

例如:相册表中会添加图片的点击数字段,在相册图片表中也会添加图片的点击数字段;

因为第三范式和关系型数据库的原因,所以,我们在查询一张表时往往不能得到所有我们需要的数据,这时候,就需要使用联查。

当然,你说我不想用联查,也没关系,可以分步单表查询,如果事先知道关系字段的值,可以直接两步就查询到结果,如果不知道的话,就需要先查出来,再用循环来查询第二张表,就会很麻烦

但是,Mysql可以直接帮我们联查两张表,只需要你知道两张表的共同字段是什么。

二、知识刨析

JOIN:select语句的条件之一,可以根据某些连接条件从其他表中获取数据。

JOIN的含义就如英文单词“join”一样,连接两张表,大致分为内连接,外连接,右连接,左连接,自然连接。

当然你也可以连接三张表四张表,但是这将大大降低数据库性能,为什么这么说呢?

这是因为所有的连接在筛选之前都会先计算所有表的笛卡尔积

笛卡尔积,也叫交叉连接:CROSS JOIN

要理解各种JOIN首先要理解笛卡尔积。笛卡尔积就是将A表的每一条记录与B表的每一条记录强行拼在一起。所以,如果A表有n条记录,B表有m条记录,笛卡尔积产生的结果就会产生n*m条记录。

下面的例子,book有10条记录,book_type有5条记录,所有他们俩的笛卡尔积有50条记录。

首先我们看一下这两张表的数据

第一张book表

第二张book_type表

孔乙己曾说过:茴香豆的茴有四种写法,我们的笛卡尔积也有四种方法得到,分别是:

SELECT * FROM book CROSS JOIN book_type;
SELECT * FROM book INNER JOIN book_type;
SELECT * FROM book,book_type;
SELECT * FROM t_blog NATURE JOIN book_type;

此时,四种语句都可以得到相同的结果,如下

可以看到这四种方法都会产生同样的结果,如果此时我用数学上的一个圆表示一张表,圆相交的部分标识两张表重合数据的话,其结果就如下韦恩图所示

那么笛卡尔积cross join就是上图中的第?张图,其实都不是,上图中并没有corss join适合的图

但是事实上我们一般不会直接来求这个,因为当两张表数据量都很大的情况下,直接求这个数据量就非常惊人了。

接下来,就是我们最常用的内连接

内连接:INNER JOIN

内连接INNER JOIN是最常用的连接操作。从数学的角度讲就是求两个表的交集,从笛卡尔积的角度讲就是从笛卡尔积中挑出ON子句条件成立的记录。有INNER JOIN,WHERE(等值连接),STRAIGHT_JOIN,JOIN(省略INNER)四种写法。

SELECT * FROM book INNER JOIN book_type ON book.type_id=book_type.type_id;
SELECT * FROM book,book_type WHERE book.type_id=book_type.type_id;
SELECT * FROM book STRAIGHT_JOIN book_type ON book.type_id=book_type.type_id; //注意STRIGHT_JOIN有个下划线
SELECT * FROM book JOIN book_type ON book.type_id=book_type.type_id;

其得到结果如下

可以看到,其只筛选了9行数据,为什么呢?因为我的筛选条件是type_id相等,而book表中有一条数据的type_id是6,在book_type表中没有记录。其在韦恩图就是中间那幅。

左连接:LEFT JOIN

左连接LEFT JOIN的含义就是求两个表的交集外加左表剩下的数据。依旧从笛卡尔积的角度讲,就是先从笛卡尔积中挑出ON子句条件成立的记录,然后加上左表中剩余的记录。

SELECT * FROM book LEFT JOIN book_type ON book.type_id=book_type.type_id;

其结果如下

可以看到,相比之前的内连接,id=8的数据出来了,只不过后面的属性都是null,因为其找不到type_id相匹配的数据。左连接在韦恩图中就是第一幅

右连接:RIGHT JOIN

同理右连接RIGHT JOIN就是求两个表的交集外加右表剩下的数据。再次从笛卡尔积的角度描述,右连接就是从笛卡尔积中挑出ON子句条件成立的记录,然后加上右表中剩余的记录。

SELECT * FROM book RIGHT JOIN book_type ON book.type_id=book_type.type_id;

其结果如下

可以看到,其也是产生10条数据,只不过和左连接不同的是其中有一条是左边数据为Null,其实在两表联查时可以用左连接的情况都可以使用右连接来代替,同理亦然,只不过将表的顺序换一下罢了。

外连接:OUTER JOIN

外连接就是求两个集合的并集。从笛卡尔积的角度讲就是从笛卡尔积中挑出ON子句条件成立的记录,然后加上左表中剩余的记录,最后加上右表中剩余的记录。另外MySQL不支持OUTER JOIN,但是我们可以对左连接和右连接的结果做UNION操作来实现。

SELECT * FROM book LEFT JOIN book_type ON book.type_id=book_type.type_id
UNION
SELECT * FROM book RIGHT JOIN book_type ON book.type_id=book_type.type_id;

结果如下

可以看到,这里产生了11条数据,因为公共部分是9条,左边剩余1,右边剩余1,那么全部加起来之后,就变成了11条数据,其相当于数学中的并集,区别于内连接的交集。

USING子句

MySQL中连接SQL语句中,ON子句的语法格式为:table1.column_name = table2.column_name。当模式设计对联接表的列采用了相同的命名样式时,就可以使用 USING 语法来简化 ON 语法,格式为:USING(column_name)。

所以,USING的功能相当于ON,区别在于USING指定一个属性名用于连接两个表,而ON指定一个条件。另外,SELECT *时,USING会去除USING指定的列,而ON不会。

select * from book inner join book_type using(type_id);

结果如下

可以看到,和内连接是一样的结果,只不过排序上有点区别。

自然连接:NATURAL JOIN

自然连接就是USING子句的简化版,它找出两个表中相同的列作为连接条件进行连接。有左自然连接,右自然连接和普通自然连接之分。

select * from book natural join book_type;
select * from book natural left join book_type;
select * from book natural right join book_type;

就不展示结果了,但是除非你也像我一样把其中的一个id列换名字或者都换名字,否则尽量不要用自然连接。

补充

从之前的图我们可以看到还有三种情况我们没有讲到,分别是4.5.7

要怎么做才能达到筛选出图4.5.7的效果呢?

图4是使用where子句来在左连接的基础上筛选where book_type.type_id is null;

SELECT * FROM book LEFT JOIN book_type ON book.type_id=book_type.type_id where book_type.type_id is null;

图5同理,只是在右连接基础上换一下就好

select * from book right join book_type on book.type_id=book_type.type_id where book.id is null;

图7的话,使用union拼接上面两句即可

三、常见问题

1.join联表查询和单表查询,效率上究竟有多大差距?

2.怎么优化呢?

3.我们应该怎么选择?

四、解决方案

其实在数据量比较小的情况下,而且联表的数据都有索引的情况下,联表查询肯定是更快的,因为分表查询还要考虑网络开销,多次调用数据库等情况。

但是在数据量特别大的情况下,而且索引优化也做的不好的话,还是单表会快一点。

所以,总的来说,还是要综合考虑各种情况。然后需要注意的是sql内联时的一个顺序

1、执行FROM语句 

2、执行ON过滤 

3、添加外部行 

4、执行where条件过滤 

5、执行group by分组语句 

6、执行having 

7、select列表 

8、执行distinct去重复数据 

9、执行order by字句 

10、执行limit字句

因为内联之后数据量是几何倍的增加,所以如果可以,有些分语句可以在单表的时候执行。

我们以后的重点其实还是在优化性能上,毕竟联表查询肯定是要方便很多的。

最后的最后,重复一遍,

不要连接三张以上的表!!!

每张表1000条数据,产生的连接数据就是1000000000条!

五、更多讨论

1.为什么说不要用or?

如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,但是使用union的话即使一个没有也不会影响整体

2.对于多表联查是先分页再查询还是先查询再分页好?

要先分页再JOIN,否则逻辑读会很高,性能很差。

3.select count(*) from table为什么不能使用,不是只是统计数量吗?

这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的

六、参考文献

https://www.cnblogs.com/fudashi/p/7491039.html

https://blog.csdn.net/Tim_phper/article/details/78344444?locationNum=9&fps=1

 

今天的分享就到这了,大家有什么问题可以评论里留言,觉得不错也可以点个赞哦~

大家有什么自己独到的SQL优化经验也可以在评论里分享一下哦

 

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值