本文将从连接的理论和语法讲起,结合具体的例子,详细分析 SQL 连接。

之前对数据库的连接操作似懂非懂,大概知道是什么东西,但是面试笔试的时候被虐成渣,讲不清连接到底是什么。吃一堑,长一智。这就是所谓的似懂非懂, 只是单纯的看书是没用的,只有亲自动手做实验才能彻底理解什么是连接。

连接类型与条件

SQL 中每一种连接操作都包括一个连接类型和连接条件。

连接类型

连接类型决定了如何处理连接条件不匹配的记录。

连接类型 返回结果
inner join 只包含左右表中满足连接条件的记录
left outer join 在内连接的基础上,加入左表中不与右表匹配的记录,剩余字段赋值为null
right outer join 在内连接的基础上,加入右表中不与左表匹配的记录,剩余字段赋值为null
full outer join 左外连接和右外连接的组合。
cross join 等价于没有连接条件的内连接(即产生笛卡尔乘积)

关键字 inner 和 outer 是可选的,因为根据连接类型的其余内容我们可以判断出连接是内连接和外连接。简单来说就是:除了单独的 join 是内连接,其他都是外连接。

对外连接来说,连接条件是 必须的 ;但对内连接来说,连接条件是 可选的 (如果省略,将产生笛卡尔积)。

连接条件

连接条件决定两个表中哪些记录互相匹配以及连接结果中出现哪些属性。

连接条件 修饰位置 语义
natural 连接类型之前 连接两个表之间的所有公共字段相等的记录,合并相同的列
on <谓词> 连接类型之后 连接符合谓词的记录,不合并相同的列
using(A1, A2,…,An) 连接类型之后 natural 语义的子集,只连接两个表中(A1,A2,..An)的公共字段,合并相同的列

从上面的描述可以看到:连接操作是连接类型和连接条件的组合,只有在这个前提下才能真正的理解连接的功能。

FQA

例子中使用到的表

student

+----+--------+
| id | name |
+----+--------+
| | 张三 |
| | 李四 |
| | 王二 |
| | 初一 |
| | 初二 |
+----+--------+
teacher +----+-----------+
| id | name |
+----+-----------+
| | 王老师 |
| | 李老师 |
| | 张老师 |
| | 肖老师 |
| | NULL |
| | 陈老师 |
+----+-----------+
course +----+--------+------+
| id | cname | tid |
+----+--------+------+
| | 数学 | |
| | 英语 | |
| | 语文 | |
| | 体育 | |
| | 物理 | NULL |
+----+--------+------+
student_course +-----+-----+
| sid | cid |
+-----+-----+
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
+-----+-----+

内连接之后的结果集数量是多少?等于左表或者右表中记录的数量吗?

内连接 teacher course 的结果

select * from teacher inner join course on teacher.id = course.tid;
+----+-----------+----+--------+------+
| id | name | id | cname | tid |
+----+-----------+----+--------+------+
| | 王老师 | | 数学 | |
| | 李老师 | | 英语 | |
| | 张老师 | | 语文 | |
| | 王老师 | | 体育 | |
+----+-----------+----+--------+------+

可以发现,王老师同时教数学和体育,因此左表中王老师匹配了右表中两条记录,物理没有老师教,所以没有出现在结果中。说明 内连接的结果集数量等于左右表中匹配记录的数量 。

左连接之后的结果集数量是多少?等于左表的记录数量吗?

左连接 teacher course 的结果

select * from teacher left join course on teacher.id = course.tid;
+----+-----------+------+--------+------+
| id | name | id | cname | tid |
+----+-----------+------+--------+------+
| | 王老师 | | 数学 | |
| | 李老师 | | 英语 | |
| | 张老师 | | 语文 | |
| | 王老师 | | 体育 | |
| | 肖老师 | NULL | NULL | NULL |
| | NULL | NULL | NULL | NULL |
| | 陈老师 | NULL | NULL | NULL |
+----+-----------+------+--------+------+

可以看到,没有教授课程的老师也出现在结果中,对应的字段都为NULL。说明结果集的数量并不等于左表记录的数量,因为两个表直接不是一对一的关系。其数量应该等于 内连接的结果集数量加上左表中不匹配的记录数量 。

Mysql 中不支持 full outer join

可以通过 union 操作模拟。

SELECT * FROM teacher
LEFT JOIN course ON teacher.id = course.tid
UNION
SELECT * FROM teacher
RIGHT JOIN course ON teacher.id = course.id; +------+-----------+------+--------+------+
| id | name | id | cname | tid |
+------+-----------+------+--------+------+
| | 王老师 | | 数学 | |
| | 李老师 | | 英语 | |
| | 张老师 | | 语文 | |
| | 王老师 | | 体育 | |
| | 肖老师 | NULL | NULL | NULL |
| | NULL | NULL | NULL | NULL |
| | 陈老师 | NULL | NULL | NULL |
| | 肖老师 | | 体育 | |
| | NULL | | 物理 | NULL |
+------+-----------+------+--------+------+
多表连接问题 考虑查出所有学生的课程的记录 select * from student
left join student_course on student.id = student_course.sid
left join course on student_course.cid = course.id; +----+--------+------+------+------+--------+------+
| id | name | sid | cid | id | cname | tid |
+----+--------+------+------+------+--------+------+
| | 张三 | | | | 数学 | |
| | 李四 | | | | 数学 | |
| | 张三 | | | | 英语 | |
| | 张三 | | | | 语文 | |
| | 李四 | | | | 体育 | |
| | 初一 | | | | 体育 | |
| | 王二 | | | | 物理 | NULL |
| | 王二 | | | NULL | NULL | NULL |
| | 初二 | NULL | NULL | NULL | NULL | NULL |
+----+--------+------+------+------+--------+------+

用学生表连接中间表,再连接课程表可以得到结果。连接操作是针对两个表之间的,所以上面的结果是从左到右,两两连接得到的。

如果你有更多关于连接的问题,或者发现文章中的错误,欢迎留言交流

参考资料

《数据库系统概念》

03--SQLtie三言两语SQLtie链接(join)的更多相关文章

  1. 2018.03.27 python pandas merge join 使用

    #2.16 合并 merge-join import numpy as np import pandas as pd df1 = pd.DataFrame({'key1':['k0','k1','k2 ...

  2. 数据库 的几种链接 join

    直接demo,懒的同学可以看看效果 两个表的数据 join和inner join一样 full join报错,可有大神知道原因?

  3. 十几张表的join(千万级/百万级表) 7hours-->5mins

    ================START============================== 来了一个mail说是job跑得很慢,调查下原因 先来看下sql: SELECT h.order_ ...

  4. sort merge join导致temp被爆菊

    SQL_ID cqsz37256v36j, child number 1 ------------------------------------- INSERT /*+append*/ INTO T ...

  5. oracle相关的知识

    01.表空间的创建与删除 Spool 目录  (把sql语句都记录在txt文件中)spool  e:\xxx.txtSpool off 结束   SQL> --清除屏幕信息SQL> cle ...

  6. 013:Rank、视图、触发器、MySQL内建函数

    一. Rank 给出不同的用户的分数,然后根据分数计算排名 (gcdb@localhost) 09:34:47 [mytest]> create table t_rank(id int,scor ...

  7. 20170621_oracle练习

    ========================= 启动Oracle ========================= --->启动OracleOraDb11g_home1TNSListene ...

  8. .Netcore 2.0 Ocelot Api网关教程(4)- 服务发现

    本文介绍Ocelot中的服务发现(Service Discovery),Ocelot允许指定一个服务发现提供器,之后将从中寻找下游服务的host和port来进行请求路由.关于服务发现的详细介绍请点击. ...

  9. sql monitor生成不了报告& FFS hint不生效两个问题思考

    事情的发生就是这么偶然,一步步的深入才能汲取到更深入的知识~~ -------------------START------------------------------------------- ...

随机推荐

  1. Ubuntu18.04 安装后应该做的事!!!

    一.WPS 进入WPS_Linux官网,下载对应的deb文件. WPS有官方的文件说明可供查看,WPS社区 WPS 的依赖关系,部分数学公式显示支持,都可以通过WPS社区查看 安装wps sudo d ...

  2. 亚马逊免费服务器搭建Discuz!论坛过程(二)

    1:  登录服务器 在实例页面,点击连接连接,按照如下步骤,即可登录服务器. 登录成功之后,如下所示.你就可以随意玩耍了. 2: 系统优化 证书登录当然安全,但是不太方便并且麻烦,本人还是习惯用户名密 ...

  3. Mysql学习总结(12)——21分钟Mysql入门教程

    21分钟 MySQL 入门教程 目录 一.MySQL的相关概念介绍 二.Windows下MySQL的配置 配置步骤 MySQL服务的启动.停止与卸载 三.MySQL脚本的基本组成 四.MySQL中的数 ...

  4. 使用timthumb.php截取文章缩略图

    wordpress自带的缩略图功能会对每次上传的所有图片根据设置的图片尺寸进行裁剪,并把原图和裁剪后的图片保存在网站空间中,图片只裁剪一次,更改设置的尺寸不会重新生成,这样不仅占用主机空间,以后改版网 ...

  5. (11)Spring Boot配置ContextPath【从零开始学Spring Boot】

    Spring boot默认是/ ,这样直接通过http://ip:port/就可以访问到index页面,如果要修改为http://ip:port/path/ 访问的话,那么需要在Application ...

  6. asp.net--CRSF

    asp.net使用了token来防止CRSF攻击 前台: 使用@Html.AntiForgeryToken(); 浏览器里面被存了一个cookie值,这个值是asp.net存给浏览器的,是readon ...

  7. asp.net常用容器

    autofac就是ioc的第三方的IOC容器 unity也是IOC容器 掌握这两个容器就可以了,非常简单

  8. Java系列之JDBC和ODBC之间的差别与联系

    JDBC简单介绍 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于运行SQL语句的Java API,它是Java十三个规范之中的一个.能够为多种关系数据 ...

  9. PHP统计所有字符在字符串中出现的次数

    <?php //统计字符串中出现的字符,出现次数 echo '<pre>'; $str = 'aaabbccqqwweedfghhjffffffffggggggggg';//字符串示 ...

  10. HDU 1754 I Hate it (线段树最大值模板)

    思路:与我发表的上一遍求和的思想一样   仅仅是如今变成求最大值而已 AC代码: #include<iostream> #include<cstdio> #include< ...