关于 JOIN 耐心总结,学不会你打我系列
现在随着各种数据库框架的盛行,在提高效率的同时也让我们忽略了很多底层的连接过程,这篇文章是对 SQL 连接过程梳理,并涉及到了现在常用的 SQL 标准。
其实标准就是在不同的时间,制定的一些写法或规范。
从 SQL 标准说起
在编写 SQL 语句前,需要先了解在不同版本的规范,因为随着版本的变化,在具体编写 SQL 时会有所不同。对于 SQL 来说,SQL92 和 SQL99 是最常见的两个 SQL 标准,92 和 99 对应其提出的年份。除此之外,还存在 SQL86、SQL89、SQL2003、SQL2008、SQL2011,SQL2016等等。
但对我们来说,SQL92 和 SQL99 是最常用的两个标准,主要学习这两个就可以了。
为了演示方便,现在数据库中加入如下三张表:
每个学生属于一个班级,通过班级的人数来对应班级的类型。
-- ----------------------------
DROP TABLE IF EXISTS `Student`;
CREATE TABLE `Student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '',
`birth` varchar(20) NOT NULL DEFAULT '',
`sex` varchar(10) NOT NULL DEFAULT '',
`class_id` int(11) NOT NULL COMMENT '班级ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of Student
-- ----------------------------
INSERT INTO `Student` VALUES ('1', '胡一', '1994.1.1', '男', '1');
INSERT INTO `Student` VALUES ('3', '王阿', '1992.1.1', '女', '1');
INSERT INTO `Student` VALUES ('5', '王琦', '1993.1.2', '男', '1');
INSERT INTO `Student` VALUES ('7', '刘伟', '1998.2.2', '女', '1');
INSERT INTO `Student` VALUES ('11', '张使', '1994.1.1', '男', '3');
INSERT INTO `Student` VALUES ('13', '王阿', '1992.1.1', '女', '3');
INSERT INTO `Student` VALUES ('15', '夏琪', '1993.1.2', '男', '3');
INSERT INTO `Student` VALUES ('17', '刘表', '1998.2.2', '女', '3');
INSERT INTO `Student` VALUES ('19', '诸葛', '1994.1.1', '男', '3');
INSERT INTO `Student` VALUES ('21', '王前', '1992.1.1', '女', '3');
INSERT INTO `Student` VALUES ('23', '王意识', '1993.1.2', '男', '3');
INSERT INTO `Student` VALUES ('25', '刘等待', '1998.2.2', '女', '3');
INSERT INTO `Student` VALUES ('27', '胡是一', '1994.1.1', '男', '5');
INSERT INTO `Student` VALUES ('29', '王阿请', '1992.1.1', '女', '5');
INSERT INTO `Student` VALUES ('31', '王消息', '1993.1.2', '男', '5');
INSERT INTO `Student` VALUES ('33', '刘全', '1998.2.2', '女', '5');
INSERT INTO `Student` VALUES ('35', '胡爱', '1994.1.1', '男', '5');
INSERT INTO `Student` VALUES ('37', '王表', '1992.1.1', '女', '5');
INSERT INTO `Student` VALUES ('39', '王华', '1993.1.2', '男', '5');
INSERT INTO `Student` VALUES ('41', '刘伟以', '1998.2.2', '女', '5');
INSERT INTO `Student` VALUES ('43', '胡一彪', '1994.1.1', '男', '5');
INSERT INTO `Student` VALUES ('45', '王阿符', '1992.1.1', '女', '5');
INSERT INTO `Student` VALUES ('47', '王琦删', '1993.1.2', '男', '5');
INSERT INTO `Student` VALUES ('49', '刘达达', '1998.2.2', '女', '5');
-- ----------------------------
-- Table structure for `Class`
-- ----------------------------
DROP TABLE IF EXISTS `Class`;
CREATE TABLE `Class` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL DEFAULT '',
`number` int(11) NOT NULL DEFAULT '',
`class_type_id` int(11) NOT NULL COMMENT '班级类型ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of Class
-- ----------------------------
INSERT INTO `Class` VALUES ('1', '1年1班', 4, '1');
INSERT INTO `Class` VALUES ('3', '1年2班', 8, '3');
INSERT INTO `Class` VALUES ('5', '1年3班', 12, '5');
CREATE TABLE `ClassType`(
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL DEFAULT '',
`minimum_number` int(11) NOT NULL DEFAULT '' COMMENT '最少的班级人数',
`maximum_number` int(11) NOT NULL DEFAULT '' COMMENT '最多的班级人数',
PRIMARY KEY(`id`)
);
INSERT INTO `ClassType` VALUES ('1', '小班', '1', '4');
INSERT INTO `ClassType` VALUES ('3', '中班', '5', '8');
INSERT INTO `ClassType` VALUES ('5', '大班', '9', '12');
SQL92
笛卡尔积(交叉连接)
笛卡尔积是一个数学上的改变,表示有 X,Y两个集合,则 X,Y的笛卡尔积,表示为 X * Y. 表示由 X,Y 组成的有序对的所有情况。对应在 SQL 会将两张表中的每一行进行组合。可将任意表连接,并且这两张表可以没有关联关系。
这里拿学生表和班级表举例,在学生表中我们插入了20名学生的数据,课程表中插入三个班级。则学生和班级的笛卡尔结果就是将两表的每行数据一一组合,最后就是有 24 * 3 = 72 行的结果,如下图所示。
并且需要知道的是,下面学习的外连接,自连接,等值连接等都是在笛卡尔积的基础上筛选得到的。
对应的 SQL92 写法为:
select * from Student, Class;
等值连接(内连接)
等值连接就是将两张表中都存在的列进行连接,具体来说就是 where 后面通过 = 进行筛选。
比如查询 Student 和其所属 Class 信息的关系:
SELECT * FROM Student as s, Class as c where s.class_id = c.id;
非等值连接
非等值连接就是将等值连接中的等号换成其他的过滤条件。
比如这里查询每个班级的信息以及所属的班级类别。
SELECT * FROM Class as c, ClassType t where c.number between t.minimum_number and maximum_number;
外连接
对于 SQL92 的外连接来说,在连接时会将两张表分为主表和从表,主表显示所有的数据,从表显示匹配到的数据,没有匹配到的则显示 None. 用 +
表示从表的位置。
左外连接:左表是主表,右表时从表。
SELECT * FROM Student as s , Class as c where s.class_id = c.id(+);
右外连接:左表是从表,右表时主表。
SELECT * FROM Class as c, Student as s where c.id = s.class_id(+);
注意 SQL92 中并没有全外连接。
自连接
自连接一般用于连接本身这张表,由于常见的 DBMS 都会对自连接做一些优化,所以一般在子查询和自连接的情况下都使用自连接。
比如想要查询比1年1班人数多的班级:
子查询:
SELECT * FROM Class WHERE number > (SELECT number FROM Class WHERE name="1年1班");
自连接:
SELECT c2.* FROM Class c1, Class c2 WHERE c1.number < c2.number and c1.name = "1年1班";
SQL99
交叉连接
SELECT * FROM Student CROSS JOIN Class;
还可以对多张表进行交叉连接,比如连接 Student,Class,ClassType 三张表,结果为 24 * 3 * 3 = 216 条。
相当于嵌套了三层 for 循环。
自然连接
其实就是 SQL92 中的等值连接,只不过连接的对象是具有相同列名,并且值也相同的内容。
SELECT * FROM Student NATURAL JOIN CLASS;
SELECT * FROM Student as s, Class as c where s.id = c.id;
如果想用 NATURAL JOIN
时,建议为两表设置相同的列名,比如 Student 表中的班级列为 class_id
, 则在 Class 表中,id 也应改为 class_id
. 这样连接更合理一些。
如果大家尝试,自然连接的话,会发现查出来的结果集为空,不要奇怪,下面说一下原因:
这是因为,NATURAL JOIN 会自动连接两张表中相同的列名,而对于 Student 和 Class 两张表来说,id 和 name 在这两张表都是相同的,所以既满足 id 又满足 name 的行是不存在的。
相当于 SQL 变成了这样
SELECT * FROM Student as s, Class as c where s.id = c.id and s.name = c.name;
ON 连接
ON 连接其实对了 SQL92 中的等值连接和非等值连接:
等值连接:
SELECT * FROM Student as s JOIN Class as c ON s.class_id = c.id;
or
SELECT * FROM Student as s INNER JOIN Class as c ON s.class_id = c.id;
非等值连接:
SELECT * FROM Class as c JOIN ClassType t ON c.number between t.minimum_number and maximum_number;
USING 连接
和 NATURAL JOIN 很像,可以手动指定具有相同列名的列进行连接:
SELECT * FROM Student JOIN Class USING(id);
这时就解决了之前列存在重名,无法连接的情况。
外连接
左外连接: 左表是主表,右表时从表。
SELECT * FROM Student as s LEFT JOIN Class as c on s.class_id = c.id;
OR
SELECT * FROM Student as s LEFT OUTER JOIN Class as c on s.class_id = c.id;
右外连接:左表是从表,右表时主表。
SELECT * FROM Student as s RIGHT JOIN Class as c on s.class_id = c.id;
OR
SELECT * FROM Student as s RIGHT OUTER JOIN Class as c on s.class_id = c.id;
全外连接: 左外连接 + 右外的连接的合集
SELECT * FROM Student as s FULL JOIN Class as c ON s.class_id = c.id;
MySQL 中没有全外连接的概念。
自连接:
SELECT c2.* FROM Class c1 JOIN Class c2 ON c1.number < c2.number and c1.name = "1年1班";
SQL92 和 SQL99 的对比
SQL92 中的等值连接(内连接),非等值连接,自连接对应了 SQL99 的 ON 连接,用于筛选满足连接条件的数据行。
SQL92 的笛卡尔积连接,对应了 SQL99 的交叉连接。
SQL92 中的外连接并不包含全外连接,而 SQL99 支持,并且将 SQL92 中 WHERE 换为 SQL99 的 ON. 这样的好处可以更清晰的表达连接表的过程,更直观。
SELECT ...
FROM table1
JOIN table2 ON filter_condition
JOIN table3 ON filter_condition
SQL99 多了自然连接和 USING 连接的过程,两者的区别是是否需要显式的指定列名。
总结
我们知道,在 SQL 中,按照年份划分了不同的标准,其中最为常用的是 SQL-92 和 SQL-99 两个标准。
接着,对比了 92 和 99 两者的不同,发现 99 的标准在连接时,更加符合逻辑并且更加直观。
最后,上一张各种连接的示意图, 方便梳理复习:
参考
关于 JOIN 耐心总结,学不会你打我系列的更多相关文章
- mysql join操作
join的类型 1. 内联结:将两个表中存在联结关系的字段符合联结关系的那些记录形成记录集的联结. 2. 外联结:分为外左联结和外右联结. 案例背景 create table java (name ...
- [转]linux 下 join命令总结
转自:http://blog.chinaunix.net/uid-20754793-id-177777.html 有两个文件需要合并,开始写了脚本实现,忽然发现join命令能够完全替代,总结了一下jo ...
- mysql5.5手册读书日记(3)
<?php /* MySQL_5.5中文参考手册 587开始 与GROUP BY子句同时使用的函数和修改程序 12.10.1. GROUP BY(聚合)函数 12.10.2. GROUP BY修 ...
- 微软BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)
[公告]本博客于2015年10月起不再更新 新博客文章主要发表在商业智能BI社区: http://www.flybi.net/blog/biwork 博客地图自动分类 文章目录方便更好的导航,阅读文章 ...
- BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)
微软 BI ETL 架构设计 如何在 ETL 项目中统一管理上百个 SSIS 包的日志和包配置框架 如何管理和记录 SSIS 各个 Task 的开始执行时间和结束时间以及 Task 中添加|删除|修改 ...
- 微软BI SSIS 2012 辅助阅读博客
大家可以根据对应的视频课程名称查找相关的辅助阅读博客,有少量辅助阅读博客和视频课程讲解内容相同,大部分都是拓展总结部分.希望大家在学完每一个视频课程之后看看相关博客内容,这样可以在知识面和深度上继续得 ...
- python中字符串(str)常用操作总结
# 字符串的常用操作方法 (都是形成新的字符串,与原字符串没有关系.) 1.字符串的基本操作之切片 s = 'python hello word' # 取首不取尾,取尾要+1 # 切片取出来的字符串与 ...
- Python笔记-备忘
一.向列表添加元素 x.append(y) #末尾添加一个元素 x.extend([y,z]) #末尾添加多个元素 x.insert(index,y) 二.向列表获取元素 x[index] 三.从列表 ...
- JUnit5学习之八:综合进阶(终篇)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
随机推荐
- EF用导航熟悉遍历从表时,删除主表出错
var entitys= Repository.Table.Where(a => ids.Contains(a.UUID)).ToList(); entitys.ForEach(a => ...
- 新来的老大,剑走偏锋,干掉AOP做操作日志,实现后我们都惊呆了
前言 用户在操作我们系统的过程中,针对一些重要的业务数据进行增删改查的时候,我们希望记录一下用户的操作行为,以便发生问题时能及时的找到依据,这种日志就是业务系统的操作日志. 本篇我们来探讨下常见操作日 ...
- Python对象组合
一个类的对象作为另一个类的对象的属性,称为类的组合. 即 class1.instance1.property = class2.instance 组合也是代码重用的重要方式之一. 先定义三个类:人.汽 ...
- 节点流(文件流) FileInputStream & FileOutputStream & FileReader & FileWriter
节点流(文件流) FileInputStream(字节流)处理视频类的 FileOutputStream(字节流) FileReader(字符流)处理文本文件 ...
- SDP架构初识
实现零信任的框架主要有SDP和Google Beyondcorp模型,前者出现在乙方的安全解决方案中,后者多作为甲方落地零信任的参考.本文主要记录对SDP技术的一些初步认识. 一.SDP简介 SDP是 ...
- 【大厂面试02期】Redis过期key是怎么样清理的?
PS:本文已收录到1.1K Star数开源学习指南--<大厂面试指北>,如果想要了解更多大厂面试相关的内容,了解更多可以看 http://notfound9.github.io/inter ...
- 【Linux】将javaweb项目部署到CentOS的tomcat上
1.将项目打包成war包 build之后war文件就生成了. 2.拷贝war文件到CentOS的tomcat的项目文件夹中 用WinSCP将文件粘帖进这个文件夹中 /wocloud/tomcat_cl ...
- Java实现 LeetCode 488 祖玛游戏
488. 祖玛游戏 回忆一下祖玛游戏.现在桌上有一串球,颜色有红色,黄色(Y),蓝色(B),绿色(G),还有白色(W). 现在你手里也有几个球. 每一次,你可以从手里的球选一个,然后把这个球插入到一串 ...
- Java实现 LeetCode 152 乘积最大子序列
152. 乘积最大子序列 给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子数组 [2,3] ...
- Java实现 LeetCode 148 排序链表
148. 排序链表 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序. 示例 1: 输入: 4->2->1->3 输出: 1->2->3-> ...