SQL-JOIN全解析

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: SQL-JOIN全解析

一、SQL JOIN的作用是什么?

SQL JOIN的作用就是把来自多个表的数据行,根据一定的规则连接起来,形成一张大的数据表。

例如下面这张用烂了的图,可以帮你快速理解每个join用法的效果:

这张图描述了left join(左连接)、right join(右连接) 、inner join(内连接)、outer join(外连接)相关的7种用法。

我改了一版:

感觉更方便理解了

可以关注我公众号,回复“mysql”,可以拿到高清大图

二、四种JOIN的区别

  • 1、INNER JOIN:如果表中有至少一个匹配,则返回行;
  • 2、LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行;
  • 3、RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行;
  • 4、FULL JOIN:只要其中一个表中存在匹配,则返回行

三、如何使用各种join

(一)准备测试数据

测试的数据很简单,依旧拿来在课堂上,书本上用到的老一套的数据表,学生表和成绩表来实现。

1、学生表:

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `sno` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学号',
  `sname` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学生姓名',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, '202001', '张三');
INSERT INTO `student` VALUES (2, '202002', '李四');
INSERT INTO `student` VALUES (3, '202003', '王五');
INSERT INTO `student` VALUES (4, '202004', '赵六');
INSERT INTO `student` VALUES (5, '202005', '小明');
INSERT INTO `student` VALUES (6, '202006', '小红');
INSERT INTO `student` VALUES (7, '202007', '小刚');
INSERT INTO `student` VALUES (8, '202008', '小李');
SET FOREIGN_KEY_CHECKS = 1;

2、成绩表:

-- ----------------------------
-- Table structure for grade
-- ----------------------------
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `sno` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '学号',
  `courseName` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '课程名',
  `grade` double(3, 0) NULL DEFAULT NULL COMMENT '成绩',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of grade
-- ----------------------------
INSERT INTO `grade` VALUES (1, '202001', '高数一', 90);
INSERT INTO `grade` VALUES (2, '202003', '高数二', 88);
INSERT INTO `grade` VALUES (3, '202003', '英语一', 77);
INSERT INTO `grade` VALUES (4, '202004', '英语二', 79);
INSERT INTO `grade` VALUES (5, '202002', 'C++语言设计', 87);
INSERT INTO `grade` VALUES (6, '202005', 'Java面向对象基础', 98);
INSERT INTO `grade` VALUES (7, '202006', '算法分析与实现', 76);
INSERT INTO `grade` VALUES (8, '202007', '软件工程A', 65);
INSERT INTO `grade` VALUES (9, '202007', '计算机应用与基础', 59);
SET FOREIGN_KEY_CHECKS = 1;

现在的数据如下:

mysql> select * from grade;
+----+--------+------------------+-------+
| id | sno    | courseName       | grade |
+----+--------+------------------+-------+
|  1 | 202001 | 高数一           |    90 |
|  2 | 202003 | 高数二           |    88 |
|  3 | 202003 | 英语一           |    77 |
|  4 | 202004 | 英语二           |    79 |
|  5 | 202002 | C++语言设计      |    87 |
|  6 | 202005 | Java面向对象基础 |    98 |
|  7 | 202006 | 算法分析与实现   |    76 |
|  8 | 202007 | 软件工程A        |    65 |
|  9 | 202007 | 计算机应用与基础 |    59 |
+----+--------+------------------+-------+
9 rows in set (0.12 sec)
mysql> 
mysql> select * from student;
+----+--------+-------+
| id | sno    | sname |
+----+--------+-------+
|  1 | 202001 | 张三  |
|  2 | 202002 | 李四  |
|  3 | 202003 | 王五  |
|  4 | 202004 | 赵六  |
|  5 | 202005 | 小明  |
|  6 | 202006 | 小红  |
|  7 | 202007 | 小刚  |
|  8 | 202008 | 小李  |
+----+--------+-------+
8 rows in set (0.12 sec)
mysql>

数据结构如下:

mysql> 
mysql> desc grade;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| sno        | varchar(20) | YES  |     | NULL    |                |
| courseName | varchar(20) | YES  |     | NULL    |                |
| grade      | double(3,0) | YES  |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
4 rows in set (0.09 sec)
mysql> 
mysql> desc student;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| sno   | varchar(20) | YES  |     | NULL    |                |
| sname | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.10 sec)
mysql>

(二)左连接

在7种join的用法中,左连接的用法有两种,如下图所示:

第一种:

mysql> select * from student t1 
    -> left join grade t2 
    -> on t1.sno=t2.sno;
+----+--------+-------+------+--------+------------------+-------+
| id | sno    | sname | id   | sno    | courseName       | grade |
+----+--------+-------+------+--------+------------------+-------+
|  1 | 202001 | 张三  |    1 | 202001 | 高数一           |    90 |
|  3 | 202003 | 王五  |    2 | 202003 | 高数二           |    88 |
|  3 | 202003 | 王五  |    3 | 202003 | 英语一           |    77 |
|  4 | 202004 | 赵六  |    4 | 202004 | 英语二           |    79 |
|  2 | 202002 | 李四  |    5 | 202002 | C++语言设计      |    87 |
|  5 | 202005 | 小明  |    6 | 202005 | Java面向对象基础 |    98 |
|  6 | 202006 | 小红  |    7 | 202006 | 算法分析与实现   |    76 |
|  7 | 202007 | 小刚  |    8 | 202007 | 软件工程A        |    65 |
|  7 | 202007 | 小刚  |    9 | 202007 | 计算机应用与基础 |    59 |
|  8 | 202008 | 小李  | NULL | NULL   | NULL             | NULL  |
+----+--------+-------+------+--------+------------------+-------+
10 rows in set (0.10 sec)
mysql>

从上面结果中可以看到我们学生中有小李,但是成绩表中并没有小李的成绩。所以会出现null的情况。



这也验证了我们前面所述的一句话:

LEFT JOIN即使右表中没有匹配,也从左表返回所有的行


即使成绩表中没有匹配的数据,也从左表返回所有的行

那么在大多数情况下,我们是不让显示null的数据的,那该怎么办?

很简单,可以调换一下t1和t2的位置即可,如下实验效果:

mysql> 
mysql> select * from grade t1 
    -> left join student t2 
    -> on t1.sno=t2.sno;
+----+--------+------------------+-------+----+--------+-------+
| id | sno    | courseName       | grade | id | sno    | sname |
+----+--------+------------------+-------+----+--------+-------+
|  1 | 202001 | 高数一           |    90 |  1 | 202001 | 张三  |
|  5 | 202002 | C++语言设计      |    87 |  2 | 202002 | 李四  |
|  2 | 202003 | 高数二           |    88 |  3 | 202003 | 王五  |
|  3 | 202003 | 英语一           |    77 |  3 | 202003 | 王五  |
|  4 | 202004 | 英语二           |    79 |  4 | 202004 | 赵六  |
|  6 | 202005 | Java面向对象基础 |    98 |  5 | 202005 | 小明  |
|  7 | 202006 | 算法分析与实现   |    76 |  6 | 202006 | 小红  |
|  8 | 202007 | 软件工程A        |    65 |  7 | 202007 | 小刚  |
|  9 | 202007 | 计算机应用与基础 |    59 |  7 | 202007 | 小刚  |
+----+--------+------------------+-------+----+--------+-------+
9 rows in set (0.16 sec)
mysql>

此处是重点:在Mysql5.7的官方手册中也提及到,这个优化的方式:

At the parser stage, queries with right outer join operations are converted to equivalent queries containing only left join operations. In the general case, the conversion is performed such that this right join:

在解析器阶段,具有右外部连接的查询会被转换为仅包含左连接操作的相等查询。

在一般情况下,左连接会转换成右连接

(T1, ...) RIGHT JOIN (T2, ...) ON P(T1, ..., T2, ...)

Becomes this equivalent left join:

变成下面这个等价的左连接:

(T2, ...) LEFT JOIN (T1, ...) ON P(T1, ..., T2, ...)

可以看到在转换的时候,会把t1变成t2,把t2的位置换成t1的位置。

第二种:

mysql> 
mysql> select * from student t1 
    -> left join grade t2 
    -> on t1.sno=t2.sno
    -> where t2.sno is null;
+----+--------+-------+------+------+------------+-------+
| id | sno    | sname | id   | sno  | courseName | grade |
+----+--------+-------+------+------+------------+-------+
|  8 | 202008 | 小李  | NULL | NULL | NULL       | NULL  |
+----+--------+-------+------+------+------------+-------+
1 row in set (19.59 sec)
mysql>

从结果上看,很清楚,只查出来了sno为null的数据。

如果not null呢?

mysql> 
mysql> select * from student t1 
    left join grade t2 
    on t1.sno=t2.sno
    where t2.sno is NOT null;
+----+--------+-------+----+--------+------------------+-------+
| id | sno    | sname | id | sno    | courseName       | grade |
+----+--------+-------+----+--------+------------------+-------+
|  1 | 202001 | 张三  |  1 | 202001 | 高数一           |    90 |
|  3 | 202003 | 王五  |  2 | 202003 | 高数二           |    88 |
|  3 | 202003 | 王五  |  3 | 202003 | 英语一           |    77 |
|  4 | 202004 | 赵六  |  4 | 202004 | 英语二           |    79 |
|  2 | 202002 | 李四  |  5 | 202002 | C++语言设计      |    87 |
|  5 | 202005 | 小明  |  6 | 202005 | Java面向对象基础 |    98 |
|  6 | 202006 | 小红  |  7 | 202006 | 算法分析与实现   |    76 |
|  7 | 202007 | 小刚  |  8 | 202007 | 软件工程A        |    65 |
|  7 | 202007 | 小刚  |  9 | 202007 | 计算机应用与基础 |    59 |
+----+--------+-------+----+--------+------------------+-------+
9 rows in set (19.46 sec)
mysql>

这就很神奇了,居然和我们上一种想要的最终结果一样。那么这个sql就是舍弃掉了为null的数据。

(三)右连接

这个的用法和左连接正好相反,可以在脑子中想想一下。

不罗嗦了,直接看效果吧。

mysql> 
mysql> select * from student t1 
    right join grade t2 
    on t1.sno=t2.sno;
+----+--------+-------+----+--------+------------------+-------+
| id | sno    | sname | id | sno    | courseName       | grade |
+----+--------+-------+----+--------+------------------+-------+
|  1 | 202001 | 张三  |  1 | 202001 | 高数一           |    90 |
|  2 | 202002 | 李四  |  5 | 202002 | C++语言设计      |    87 |
|  3 | 202003 | 王五  |  2 | 202003 | 高数二           |    88 |
|  3 | 202003 | 王五  |  3 | 202003 | 英语一           |    77 |
|  4 | 202004 | 赵六  |  4 | 202004 | 英语二           |    79 |
|  5 | 202005 | 小明  |  6 | 202005 | Java面向对象基础 |    98 |
|  6 | 202006 | 小红  |  7 | 202006 | 算法分析与实现   |    76 |
|  7 | 202007 | 小刚  |  8 | 202007 | 软件工程A        |    65 |
|  7 | 202007 | 小刚  |  9 | 202007 | 计算机应用与基础 |    59 |
+----+--------+-------+----+--------+------------------+-------+
9 rows in set (19.49 sec)
mysql>

从上面效果上可以看到只匹配到了成绩表中有的数据,小李就没有显示。

也验证了:

RIGHT JOIN即使左表中没有匹配,也从右表返回所有的行

(四)内连接

INNER JOIN:如果表中有至少一个匹配,则返回行;

mysql> select * from student t1 
    inner join grade t2 
    on t1.sno=t2.sno;
+----+--------+-------+----+--------+------------------+-------+
| id | sno    | sname | id | sno    | courseName       | grade |
+----+--------+-------+----+--------+------------------+-------+
|  1 | 202001 | 张三  |  1 | 202001 | 高数一           |    90 |
|  3 | 202003 | 王五  |  2 | 202003 | 高数二           |    88 |
|  3 | 202003 | 王五  |  3 | 202003 | 英语一           |    77 |
|  4 | 202004 | 赵六  |  4 | 202004 | 英语二           |    79 |
|  2 | 202002 | 李四  |  5 | 202002 | C++语言设计      |    87 |
|  5 | 202005 | 小明  |  6 | 202005 | Java面向对象基础 |    98 |
|  6 | 202006 | 小红  |  7 | 202006 | 算法分析与实现   |    76 |
|  7 | 202007 | 小刚  |  8 | 202007 | 软件工程A        |    65 |
|  7 | 202007 | 小刚  |  9 | 202007 | 计算机应用与基础 |    59 |
+----+--------+-------+----+--------+------------------+-------+
9 rows in set (0.12 sec)
mysql>

(五)外连接

这一种在Mysql中是不支持的,可以在SQL Server上测试。这里就不测试了。

四、总结

  • 1、INNER JOIN:如果表中有至少一个匹配,则返回行;
  • 2、LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行;
  • 3、RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行;
  • 4、FULL JOIN:只要其中一个表中存在匹配,则返回行


目录
相关文章
|
1月前
|
SQL IDE 数据库连接
IntelliJ IDEA处理大文件SQL:性能优势解析
在数据库开发和管理工作中,执行大型SQL文件是一个常见的任务。传统的数据库管理工具如Navicat在处理大型SQL文件时可能会遇到性能瓶颈。而IntelliJ IDEA,作为一个强大的集成开发环境,提供了一些高级功能,使其在执行大文件SQL时表现出色。本文将探讨IntelliJ IDEA在处理大文件SQL时的性能优势,并与Navicat进行比较。
34 4
|
1月前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
2月前
|
SQL 监控 数据库
SQL语句是否都需要解析及其相关技巧和方法
在数据库管理中,SQL(结构化查询语言)语句的使用无处不在,它们负责数据的查询、插入、更新和删除等操作
|
1月前
|
SQL 监控 安全
员工上网行为监控软件:SQL 在数据查询监控中的应用解析
在数字化办公环境中,员工上网行为监控软件对企业网络安全和管理至关重要。通过 SQL 查询和分析数据库中的数据,企业可以精准了解员工的上网行为,包括基础查询、复杂条件查询、数据统计与分析等,从而提高网络管理和安全防护的效率。
30 0
|
2月前
|
SQL 存储 数据库
SQL语句是否都需要解析及其相关技巧与方法
在数据库管理系统中,SQL(Structured Query Language)语句作为与数据库交互的桥梁,其执行过程往往涉及到一个或多个解析阶段
|
2月前
|
SQL 数据可视化 BI
SQL语句及查询结果解析:技巧与方法
在数据库管理和数据分析中,SQL语句扮演着至关重要的角色
|
2月前
|
SQL 监控 关系型数据库
SQL错误代码1303解析与处理方法
在SQL编程和数据库管理中,遇到错误代码是常有的事,其中错误代码1303在不同数据库系统中可能代表不同的含义
|
2月前
|
SQL 存储 关系型数据库
SQL默认索引是什么:深入解析与技巧
在SQL数据库中,索引是一种用于提高查询性能的重要数据结构
|
2月前
|
SQL 开发框架 .NET
ASP.NET连接SQL数据库:实现过程与关键细节解析an3.021-6232.com
随着互联网技术的快速发展,ASP.NET作为一种广泛使用的服务器端开发技术,其与数据库的交互操作成为了应用开发中的重要环节。本文将详细介绍在ASP.NET中如何连接SQL数据库,包括连接的基本概念、实现步骤、关键代码示例以及常见问题的解决方案。由于篇幅限制,本文不能保证达到完整的2000字,但会确保
|
2月前
|
SQL 分布式计算 大数据
大数据-97 Spark 集群 SparkSQL 原理详细解析 Broadcast Shuffle SQL解析过程(一)
大数据-97 Spark 集群 SparkSQL 原理详细解析 Broadcast Shuffle SQL解析过程(一)
78 0

推荐镜像

更多