left join,right join,inner join,full join之间的区别 UNION UNION ALL

描述:今天在写sql的时候 就是在写left 的时候 看到结果记录数的时候 自己陷入沉思~因为自己想到了之前的面试题 关于sql连接的 时间过去很久 一直也是半知半解 没有去深入思考。

待翻新待翻新 有些地方太绕了

待翻新待翻新 有些地方太绕了

待翻新待翻新 有些地方太绕了

关于是哪个sql语句的联系引发了这篇博客,就是下面的

题解 | #月总刷题数和日均刷题数#_牛客博客 (nowcoder.net)

但是今天理解的全连接和union union all用法和下面的链接不敢苟同 仅代表个人理解 

MySQL全连接(Full Join)实现,union和union all用法 - youxin - 博客园

通过查询结果集来看的话 union 乃至union all | 区别在于是否存在重复数据 是A表和B表的结果集纵向合并 full join 则是两个表根据关联字段横向合并

一、面试题

A表目前有100万数据, B表10条数据,根据关联键匹配,A表中的数据与B表中的数据关联关系一一对应,A表中能关联上48万数据,B表中能关联上的数据为5条。那么问: A inner join B、 A left join B、 A right join B、 A full join B 的连接方式,最终查询出的数据量分别是多少

 想了一下答案放在最后面的 作用就是可以用这个答案反向检测自己所思所想是否合理 有无错误

全篇文章 分为理论实战两大维度,每个维度中,分为四个小节,分别对应SQL中的 inner joinleft joinright joinfull join,文章的最后会有总结 将自己个人理解的"连接公式" 附上

二、SQL_理论

2.1 SQL连接概念


INNER JOIN // 必须根据连接条件进行匹配。如果找不到匹配项,查询将返回一个空结果集 通俗点说就是只返回两个表中联结字段相等的行。

LEFT JOIN // 左边的表的记录全部找出来。系统会先用表A和表B做个笛卡儿积,然后以表A为基表,去掉笛卡儿积中表A部分为NULL的记录。最后形成你的结果。


 RIGHT JOIN // 同理left join 比如说 A表 left join B表 = B表 LEFT JOIN A 表 = A表 right join B表 

A right join B,以B表为主体,关联查询A表。

FULL JOIN // 返回两个表中匹配的所有行记录 也可以理解为左连接和右连接的结合

注释:在某些数据库中, FULL JOIN 称为 FULL OUTER JOIN。

2.2 笛卡尔积概念

笛卡尔积又叫笛卡尔乘积,是一个叫笛卡尔的人提出来的。简单的说就是两个集合相乘的结果。
具体的定义去看看有关代数系的书的定义。直观的说就是集合A{a1,a2,a3} 集合B{b1,b2},他们的 笛卡尔积 是 A*B ={(a1,b1),(a1,b2),(a2,b1),(a2,b2),(a3,b1),(a3,b2)} 任意两个元素结合在一起

2.3 连接公式

A表重复数据 * B表重复数据 + left join(A) 未重复数据量 公式仅适用于left join right join

解释:如果使用left join 连接的话 那么返回的记录集行数就是A表重复数据 * B表重复数据 + A表未重复的数据

2.4 版本

MYSQL数据库版本

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第1张图片

解释:不知道从什么时候 自己也开始关注版本问题 因为有的时候 确实存在版本不对 导致结果、语法不对的情况

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第2张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第3张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第4张图片

三、SQL_实战

3.1 准备数据

user_info表数据

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `uid` int(11) NOT NULL COMMENT '用户ID',
  `nick_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称',
  `achievement` int(11) NULL DEFAULT NULL COMMENT '成就值',
  `level` int(11) NULL DEFAULT NULL COMMENT '用户等级',
  `job` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '职业方向',
  `register_time` datetime(0) NULL DEFAULT NULL COMMENT '注册时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `uid`(`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_info
-- ----------------------------
INSERT INTO `user_info` VALUES (1, 1001, '牛客1号', 3100, 7, '算法', '2020-01-01 10:00:00');
INSERT INTO `user_info` VALUES (2, 1002, '牛客2号', 2100, 6, '算法', '2020-01-01 10:00:00');
INSERT INTO `user_info` VALUES (3, 1003, '牛客3号', 1500, 5, '算法', '2020-01-01 10:00:00');
INSERT INTO `user_info` VALUES (4, 1004, '牛客4号', 1100, 4, '算法', '2020-01-01 10:00:00');
INSERT INTO `user_info` VALUES (5, 1005, '牛客5号', 1600, 6, 'C++', '2020-01-01 10:00:00');
INSERT INTO `user_info` VALUES (6, 1006, '牛客6号', 3000, 6, 'C++', '2020-01-01 10:00:00');

SET FOREIGN_KEY_CHECKS = 1;

 exam_record、practice_record表数据

drop table if exists practice_record;
CREATE TABLE  practice_record (
    id int PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
    uid int NOT NULL COMMENT '用户ID',
    question_id int NOT NULL COMMENT '题目ID',
    submit_time datetime COMMENT '提交时间',
    score tinyint COMMENT '得分'
)CHARACTER SET utf8 COLLATE utf8_general_ci;

drop table if exists exam_record;
CREATE TABLE exam_record (
    id int PRIMARY KEY AUTO_INCREMENT COMMENT '自增ID',
    uid int NOT NULL COMMENT '用户ID',
    exam_id int NOT NULL COMMENT '试卷ID',
    start_time datetime NOT NULL COMMENT '开始时间',
    submit_time datetime COMMENT '提交时间',
    score tinyint COMMENT '得分'
)CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO practice_record(uid,question_id,submit_time,score) VALUES
(1001, 8001, '2021-08-02 11:41:01', 60),
(1002, 8001, '2021-09-02 19:30:01', 50),
(1002, 8001, '2021-09-02 19:20:01', 70),
(1002, 8002, '2021-09-02 19:38:01', 70),
(1003, 8001, '2021-08-02 19:38:01', 70),
(1003, 8001, '2021-08-02 19:48:01', 90),
(1003, 8002, '2021-08-01 19:38:01', 80);

INSERT INTO exam_record(uid,exam_id,start_time,submit_time,score) VALUES
(1001, 9001, '2021-09-01 09:01:01', '2021-09-01 09:41:01', 81),
(1002, 9002, '2021-09-01 12:01:01', '2021-09-01 12:31:01', 70),
(1002, 9001, '2021-09-01 19:01:01', '2021-09-01 19:40:01', 80),
(1002, 9002, '2021-09-01 12:01:01', '2021-09-01 12:31:01', 70),
(1004, 9001, '2021-09-01 19:01:01', '2021-09-01 19:40:01', 85),
(1002, 9002, '2021-09-01 12:01:01', null, null);

3.2 INNER JOIN

好的 首先我的思路是这样的 简单两个查询 查出两个数据库中的重复数据 然后使用INNER JOIN 得到记录集行数 找出公式规律 然后使用公式套入面试题中验证是否正确,

简单画个丑陋的表格exam_record、practice_record 将两个表表数据取出来展示给大家

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第5张图片 NO.1_两表数据图

 通过查看`NO.1_两表数据` 得知

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第6张图片 NO.2_两表总数据图

exam_record(一共6条记录)、practice_record(一共7条记录)两表inner join 也就是笛卡尔积 6*7 

inner join之后就是42条数据,这个查询是没有太大意义的 仅仅是为了亲测理论笛卡尔积 不要相信任何人说的任何话 时刻怀疑然后自己亲手测试才可以

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第7张图片 NO.3_笛卡尔积图

 然后使用inner join 连接查询 

先来看 exam_record、practice_record的内连接
select * from exam_record inner join practice_record using(uid)  返回记录集行数为13 哎???

注意我这里using(uid) 外键uid

怎么感觉怪怪的 不是 INNER JOIN  只返回两个表中联结字段相等的行吗 exam_record表和practice_record并没有字段相等13行数据啊 

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第8张图片 NO.4_inner join连接图

 别急 这个地方有个小误区 就是结论:INNER JOIN  只返回两个表中联结字段相等的行是建立在两个表using(字段) 这个字段必须不重复 才能返回相等的行 我不知道这么解释能不能解释明白 我在这个inner join 多举几个例子吧

3.2.1 例子1

select * from exam_record inner join practice_record using(id)

3.2.2 例子2

select 
* from (select distinct uid from practice_record) te
inner join (select distinct uid from exam_record) temp using(uid)

3.2.3 例子3

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第9张图片 NO.5_inner join举例图

 select * from user_info inner join exam_record using(id) 效果等同

select * from A inner join B on a.uid = b.id

inner join using(字段) 这个字段必须保证唯一性 不重复 那么数据的返回行数如果A表有重复 就返回重复的数据行数 如果没有 那么返回行数是就是A表的数据行数

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第10张图片 NO.6_inner join举例图

好了 再来看面试题 注意题中“A表中能关联上48万数据” 就是A表连接B表的时候A表重复数据48W的意思

A表目前有100万数据, B表10条数据,根据关联键匹配,A表中的数据与B表中的数据关联关系一一对应,A表中能关联上48万数据,B表中能关联上的数据为5条。那么问: A inner join B

数据库中的字段ID都是唯一性 不可能重复 那么A inner join B返回行数就是 A表的重复数据行数

48W没错了

面试题A inner join B标准答案:48w

3.3 LEFT JOIN 

来看exam_record、practice_record

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第11张图片 NO.7_left join连接图

 到这里我改变注意了 我想直接用公式套用 然后 在使用INNER JOIN连接展示数据了

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第12张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第13张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第14张图片

OKOK 口算一下 exam_record 重复数据4条 未重复数据3条 practice_record 重复数据2

4x3+2 = 14  sql语句运行 没有问题 

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第15张图片 NO.8_left join测试图

看理论:LEFT JOIN // 左边的表的记录全部找出来。系统会先用表A和表B做个笛卡儿积,然后以表A为基表,去掉笛卡儿积中表A部分为NULL的记录。最后形成你的结果。

其实这个公式就是相当于 A表和B表重复部分做笛卡尔积 不重复部分直接+上就可以了

真的就是这个图哎!!!! 

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第16张图片 NO.9_left join示例图

 大佬就是大佬 

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第17张图片 NO.10_大佬图

 OKOK 来看面试题

A表目前有100万数据, B表10条数据,根据关联键匹配,A表中的数据与B表中的数据关联关系一一对应,A表中能关联上48万数据,B表中能关联上的数据为5条。那么问: A left join B最终查询出的数据量分别是多少

分析:A表和B表重复数据为48 + 不重复数据(100-48) = 100W

面试题A left join B标准答案:100w

3.4 RIGHT JOIN

这个right join 道理同left join 相同 emmm....这里就不写那么详细了 就把示例图 自己理解着画一下 然后公式套用对一下面试题就好了

 再来看exam_record、practice_record,依旧是这两张数据库表

select * from exam_record right join practice_record using(uid)

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第18张图片 NO.7_left join连接图

公式套用:因为是right join 所以本次将以right join右边的数据表practice_record  为标准

右边数据库表重复数据 条 未重复数据 4

3*4 + 4 = 16 条数据

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第19张图片 NO.11_right join测试图

 现在看在公式是没有问题的 完全适用 要不然再来试一下 exam_record、和user_info表

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第20张图片 NO.12_right join示例图

 计划是这么一个sql

select * from user_info right join exam_record using(uid)

分析:A right join B 公式套用的话 就是 以B表为基表(标准) 也就是(重复数据) 1x4 + 2(不重复数据) = 6

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第21张图片 NO.13_right join举例图

 将表调换位置继续套用公式

select * from exam_record right join user_info using(uid)

分析: 4 x 1 + 5 = 9

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第22张图片 NO.14_right join举例图

 OK 证明公式适用于left join、right join 没问题 

画个示例图

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第23张图片 NO.15_right join示例图

继续来看面试题

A表目前有100万数据, B表10条数据,根据关联键匹配,A表中的数据与B表中的数据关联关系一一对应,A表中能关联上48万数据,B表中能关联上的数据为5条。那么问:  A right join B 的连接方式,最终查询出的数据量分别是多少

公式:B表重复数据(5) + B表未重复数据 (100-A表重复的数据 = B表未重复数据) 这个B表未重复数据可能第一次看有点绕 但是多读两遍就明白了 

举个简单的例子  你有苹果、香蕉,我只有你手中的苹果 那不还可以直接的说 我想要你手里面的香蕉嘛 因为你有的(重复) 我就没有(不重复)

 结果就是 5 +(100-48) 5+52W

 我还是坚信我自己测试的结果  毕竟下面的48W+5 哎...算了 这个问题 ,自己暂时坚持5 + 52W这个结果

特喵的 我现在有点怀疑 这个标准答案都有点不对了 (有没有大佬能解释下 这怎么回事.......)

面试题A right join B标准答案:48w+5条

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第24张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第25张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第26张图片

3.5 FULL JOIN

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第27张图片 NO.2_两表总数据图

这个full join连接 说实话 到现在对于结论FULL JOIN // 返回两个表中匹配的所有行记录 也可以理解为左连接和右连接的结合 依旧是

自己刚开始是使用left join 查一下 14条记录 一条为null

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第28张图片

然后 使用right join 再查 16条记录 3条为null 

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第29张图片

 就在刚刚我好像明白了 这个full join 是全连接 结合示例图来看的话 就容易理解了 

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第30张图片

 意为A 表B表无null 

 也就是

select * from exam_record full join  practice_record using(uid) 等同 下面的代码

select * from exam_record left join practice_record using(uid) where question_id is not null

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第31张图片

根据exam_record表数据practice_record表数据 结合full join可以得出 结果集行数

自己理解的全连接就是筛选掉left join的连接 或者既然是左右连接的结合 通过结果集行数来看 也确实 左右连接的结合 但是如果看一眼标准答案的话 

可以理解为A表数据 + B表重复数据 

面试题A full join B标准答案:100w+5条

五、总结

自己的full join 某些地方理解不够到位  还有这个面试题A right join B标准答案:48w+5条 结果 就很无语 可能会面随着经验的提升 这些问题 都会在某一天就释然了,欢迎评论区讨论批评指正!!!

left join,right join,inner join,full join之间的区别 UNION UNION ALL_第32张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第33张图片left join,right join,inner join,full join之间的区别 UNION UNION ALL_第34张图片

六、参考文章

数据库之笛卡尔积_dingxie1963的博客-CSDN博客

SQL FULL JOIN 关键字

SQL 连接_道亦的博客-CSDN博客_sql连接

full join 全连接_存是去非的博客-CSDN博客_full join 全连接

图解 SQL 中各种连接 JOIN - 知乎

SQL FULL OUTER JOIN 关键字 | 菜鸟教程

你可能感兴趣的:(数据库,sql,数据库,面试)