SQL-JOIN全解析
SQL-JOIN全解析
- 一、SQL JOIN的作用是什么?
- 二、四种JOIN的区别
- 三、如何使用各种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;
123456789101112131415161718192021222324
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;
123456789101112131415161718192021222324252627
现在的数据如下:
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>
123456789101112131415161718192021222324252627282930313233
数据结构如下:
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>
123456789101112131415161718192021222324
(二)左连接
在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>
1234567891011121314151617181920
从上面结果中可以看到我们学生中有小李,但是成绩表中并没有小李的成绩。所以会出现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>
1234567891011121314151617181920
此处是重点:在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, ...)
1
Becomes this equivalent left join: 变成下面这个等价的左连接:
(T2, ...) LEFT JOIN (T1, ...) ON P(T1, ..., T2, ...)
1
可以看到在转换的时候,会把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>
12345678910111213
从结果上看,很清楚,只查出来了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>
123456789101112131415161718192021
这就很神奇了,居然和我们上一种想要的最终结果一样。那么这个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>
1234567891011121314151617181920
从上面效果上可以看到只匹配到了成绩表中有的数据,小李就没有显示。 也验证了:
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>
12345678910111213141516171819
(五)外连接
这一种在Mysql中是不支持的,可以在SQL Server上测试。这里就不测试了。
四、总结
- 1、
INNER JOIN
:如果表中有至少一个匹配,则返回行; - 2、
LEFT JOIN
:即使右表中没有匹配,也从左表返回所有的行; - 3、
RIGHT JOIN
:即使左表中没有匹配,也从右表返回所有的行; - 4、
FULL JOIN
:只要其中一个表中存在匹配,则返回行 。
欢迎一起学习,一起交流,一起进步。
- Joomla未授权创建特权用户漏洞(CVE-2016-8869)分析
- 检测本地文件躲避安全分析
- 如何在Kerberos的Linux上安装及配置Impala的ODBC驱动
- 对抗静态分析——so文件的加密
- Bypass unsafe-inline mode CSP
- Joomla未授权创建用户漏洞(CVE-2016-8870)分析
- 如何将HDFS文件系统挂载到Linux本地文件系统
- 使用 XML 内部实体绕过 Chrome 和 IE 的 XSS 过滤器
- 响应式编程的实践
- S2-045 原理初步分析(CVE-2017-5638)
- 如何在HDFS上查看YARN历史作业运行日志
- 面向流的设计思想
- 天才第一步 Docker 纸尿裤
- 如何迁移Kudu1.2的WAL和Data目录
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP设计模式之策略模式原理与用法实例分析
- python使用QQ邮箱实现自动发送邮件
- php使用lua+redis实现限流,计数器模式,令牌桶模式
- PHP whois查询类定义与用法示例
- Laravel多域名下字段验证的方法
- ThinkPHP框架整合微信支付之Native 扫码支付模式二图文详解
- 简单了解如何封装自己的Python包
- python求解汉诺塔游戏
- Python第三方包PrettyTable安装及用法解析
- 如何让python的运行速度得到提升
- 在keras中对单一输入图像进行预测并返回预测结果操作
- python中数字是否为可变类型
- ThinkPHP3.2.3框架实现的空模块、空控制器、空操作,跳转到错误404页面图文详解
- PHP示例演示发送邮件给某个邮箱
- PHP设计模式之观察者模式定义与用法分析