描述:今天在写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 join 、left join 、right join、full join,文章的最后会有总结 将自己个人理解的"连接公式" 附上
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。
笛卡尔积又叫笛卡尔乘积,是一个叫笛卡尔的人提出来的。简单的说就是两个集合相乘的结果。
具体的定义去看看有关代数系的书的定义。直观的说就是集合A{a1,a2,a3} 集合B{b1,b2},他们的 笛卡尔积 是 A*B ={(a1,b1),(a1,b2),(a2,b1),(a2,b2),(a3,b1),(a3,b2)} 任意两个元素结合在一起
A表重复数据 * B表重复数据 + left join(A) 未重复数据量 公式仅适用于left join right join
解释:如果使用left join 连接的话 那么返回的记录集行数就是A表重复数据 * B表重复数据 + A表未重复的数据
MYSQL数据库版本
解释:不知道从什么时候 自己也开始关注版本问题 因为有的时候 确实存在版本不对 导致结果、语法不对的情况
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);
好的 首先我的思路是这样的 简单两个查询 查出两个数据库中的重复数据 然后使用INNER JOIN 得到记录集行数 找出公式规律 然后使用公式套入面试题中验证是否正确,
简单画个丑陋的表格exam_record、practice_record 将两个表表数据取出来展示给大家
NO.1_两表数据图通过查看`NO.1_两表数据` 得知
NO.2_两表总数据图exam_record(一共6条记录)、practice_record(一共7条记录)两表inner join 也就是笛卡尔积 6*7
inner join之后就是42条数据,这个查询是没有太大意义的 仅仅是为了亲测理论笛卡尔积 不要相信任何人说的任何话 时刻怀疑然后自己亲手测试才可以
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行数据啊
NO.4_inner join连接图别急 这个地方有个小误区 就是结论:INNER JOIN 只返回两个表中联结字段相等的行是建立在两个表using(字段) 这个字段必须不重复 才能返回相等的行 我不知道这么解释能不能解释明白 我在这个inner join 多举几个例子吧
select * from exam_record inner join practice_record using(id)
select
* from (select distinct uid from practice_record) te
inner join (select distinct uid from exam_record) temp using(uid)
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表的数据行数
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
来看exam_record、practice_record
NO.7_left join连接图到这里我改变注意了 我想直接用公式套用 然后 在使用INNER JOIN连接展示数据了
OKOK 口算一下 exam_record 重复数据4条 未重复数据3条 practice_record 重复数据2条
4x3+2 = 14 sql语句运行 没有问题
NO.8_left join测试图看理论:LEFT JOIN // 左边的表的记录全部找出来。系统会先用表A和表B做个笛卡儿积,然后以表A为基表,去掉笛卡儿积中表A部分为NULL的记录。最后形成你的结果。
其实这个公式就是相当于 A表和B表重复部分做笛卡尔积 不重复部分直接+上就可以了
真的就是这个图哎!!!!
NO.9_left join示例图大佬就是大佬
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
这个right join 道理同left join 相同 emmm....这里就不写那么详细了 就把示例图 自己理解着画一下 然后公式套用对一下面试题就好了
再来看exam_record、practice_record,依旧是这两张数据库表
NO.7_left join连接图select * from exam_record right join practice_record using(uid)
公式套用:因为是right join 所以本次将以right join右边的数据表practice_record 为标准
右边数据库表重复数据 3 条 未重复数据 4 条
3*4 + 4 = 16 条数据
NO.11_right join测试图现在看在公式是没有问题的 完全适用 要不然再来试一下 exam_record、和user_info表
NO.12_right join示例图计划是这么一个sql
select * from user_info right join exam_record using(uid)
分析:A right join B 公式套用的话 就是 以B表为基表(标准) 也就是(重复数据) 1x4 + 2(不重复数据) = 6
NO.13_right join举例图将表调换位置继续套用公式
select * from exam_record right join user_info using(uid)
分析: 4 x 1 + 5 = 9
NO.14_right join举例图OK 证明公式适用于left join、right join 没问题
画个示例图
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条
这个full join连接 说实话 到现在对于结论FULL JOIN // 返回两个表中匹配的所有行记录 也可以理解为左连接和右连接的结合 依旧是
自己刚开始是使用left join 查一下 14条记录 一条为null
然后 使用right join 再查 16条记录 3条为null
就在刚刚我好像明白了 这个full join 是全连接 结合示例图来看的话 就容易理解了
意为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
根据exam_record表数据practice_record表数据 结合full join可以得出 结果集行数
自己理解的全连接就是筛选掉left join的连接 或者既然是左右连接的结合 通过结果集行数来看 也确实 左右连接的结合 但是如果看一眼标准答案的话
可以理解为A表数据 + B表重复数据
面试题A full join B标准答案:100w+5条
自己的full join 某些地方理解不够到位 还有这个面试题A right join B标准答案:48w+5条 结果 就很无语 可能会面随着经验的提升 这些问题 都会在某一天就释然了,欢迎评论区讨论批评指正!!!
数据库之笛卡尔积_dingxie1963的博客-CSDN博客
SQL FULL JOIN 关键字
SQL 连接_道亦的博客-CSDN博客_sql连接
full join 全连接_存是去非的博客-CSDN博客_full join 全连接
图解 SQL 中各种连接 JOIN - 知乎
SQL FULL OUTER JOIN 关键字 | 菜鸟教程