SQL 中的左连接魔法:你真的会 left join 么?
wptr33 2024-11-21 22:05 31 浏览
(一)Left Join 的含义与特点
在数据库编程中,Left Join(左连接)具有独特的含义和特点。它以左表为基础,无论右表中是否有与之匹配的行,都会从左表返回所有的行。当右表中没有匹配的行时,相应的列会以 null 值填充。这一特点使得 Left Join 在数据查询和分析中非常有用,尤其是当我们需要确保左表中的数据不被遗漏时。例如,在一个电商数据库中,有订单表和客户表。如果我们想获取所有客户的信息以及他们的订单情况,使用 Left Join 可以确保即使某些客户没有下过订单,他们的信息也会被包含在查询结果中。
(二)基本语法与示例
Left Join 的基本语法通常为:“SELECT 列名 FROM 左表 LEFT JOIN 右表 ON 左表.列名 = 右表.列名”。例如,假设有两个表,学生表(students)包含学生的 ID 和姓名,成绩表(grades)包含学生的 ID 和成绩。要查询所有学生以及他们的成绩(如果有),可以使用以下语句:“SELECT students.ID, students.Name, grades.Grade FROM students LEFT JOIN grades ON students.ID = grades.ID”。查询结果中,对于有成绩的学生,会显示相应的成绩,而对于没有成绩的学生,成绩列会显示为 null。
再比如,在 MySQL 中,有文章表(article)和用户表(user)。文章表包含文章的 ID、标题和作者 ID,用户表包含用户的 ID、用户名和邮箱。要列出所有的文章及对应的所属用户,即使没有用户的文章也列出,可以使用如下语句:“SELECT article.aid,article.title,user.username FROM article LEFT JOIN user ON article.uid = user.uid”。返回的查询结果中,对于有对应用户的文章,会显示用户的用户名,而对于没有对应用户的文章,用户名列会显示为 null。
二、Left Join 的注意事项
(一)筛选条件的位置影响
在使用 Left Join 时,筛选条件放置的位置不同会导致结果产生差异。如果将筛选条件放在 ON 子句中,它主要是对右表进行条件过滤,但依然会返回左表的所有行,右表中没有匹配的则补为 NULL。例如,“SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id AND table2.name='特定名称'”,这里的条件只会影响右表的匹配内容,不影响返回行数,即左表的所有行都会被展示出来。
而如果将筛选条件放在 WHERE 子句中,是在临时表生成好后,再对临时表进行过滤。这时已经没有 Left Join 必须返回左边表记录的含义了,条件不为真的就全部过滤掉。例如,“SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id WHERE table2.name='特定名称'”,如果右表中没有满足这个条件的记录,那么左表中在右表无匹配行的记录也可能会被过滤掉。
(二)多表连接时的考量
在多个 Left Join 连接时,记录条数会发生变化,并且数据库优化器的执行顺序也需要考虑。多个 Left Join 连接时,执行顺序一般是先将左表与第一个右表进行连接,生成一个虚拟表,然后这个虚拟表再与下一个右表进行连接。例如有三个表 table1、table2、table3,执行“SELECT table1.a, table1.b, table1.c FROM table1 LEFT JOIN table2 ON table1.uid = table2.uid LEFT JOIN talbe3 ON table1.uid = table3.uid”,先将 table1 和 table2 组合成一个虚拟表,然后这个虚拟表再和 table3 关联。
关于记录条数,在没有 WHERE 语句情况下,结果条数一般会大于等于主表(即第一个左表)的条数。这是因为如果 SELECT 后面的字段均来自主表,并且所有的关联条件都是为了匹配主表。例如,假设表 A 有 100 条记录,表 B 和表 C 分别有 80 条和 60 条记录,在没有 WHERE 语句的多个 Left Join 连接下,结果条数可能会大于等于 100 条,具体取决于表之间的关联情况。如果 B 表中某个关联关键字出现了 N 次,那么在 A 表中与这个关联关键字相关的记录也会重复 N 次,从而导致结果条数增加。数据库优化器是最终决定执行顺序的地方,但一般会按照 Left Join 的顺序执行,但也不保证完全是那样。
三、Left Join 的实际应用
(一)解决一对多问题
当左表与右表存在一对多关系时,可以采用不同的方法来处理。例如,假设我们有一个用户表和一个订单表,用户表中的一个用户可能在订单表中有多个订单。如果业务需求是查询用户的下单量,可以将 SQL 语句编程如下:
SELECT a.用户 id,a.用户姓名,count(订单 id)FROM 用户表 a
LEFT JOIN 订单表 b ON a.用户 id = b.用户 id
GROUP BY a.用户 id,a.用户姓名;
通过这种方式,将一对多的问题转化为聚合查询,统计出每个用户的订单数量。
如果业务需求是查询用户的下单明细,可以将 SQL 语句编程如下:
SELECT a.订单 id,a.业务类型,a.单价,a.消费数量,b.用户姓名,b.用户联系方式FROM 订单表 a
LEFT JOIN 用户表 b ON a.用户 id = b.用户 id;
这样就将一对多的问题转化成多对一的问题,展示每个订单的详细信息以及对应的用户信息。
(二)条件联接查询
在面对大数据表时,运用 where 子句限定条件可以提高查询效率。有两种限定方式,一种是全量查询后 + where 子句,另一种是联接前就先做限定。
全量查询后 + where 子句的方式,如查询用户为男,消费总价大于 1w 元的用户明细:
SELECT a.订单 id,a.业务类型,a.单价,a.消费数量,b.用户姓名,b.用户联系方式FROM 订单表 a
LEFT JOIN 用户表 b ON a.用户 id = b.用户 id
WHERE b.用户性别 = '男' AND (a.单价 * a.消费数量) > 10000 AND b.用户 id is not null;
联接前就先做限定的方式,如我们需要查询用户在某时间段,访问 A 页面再访问 B 页面的留存用户量,查询的表只有用户行为表:
SELECT count(a.user_id),count(b.user_id)FROM (SELECT distinct user_id FROM 用户行为表 WHERE date_str = '2016 - 11 - 01' AND url = ‘A 页面路径’) a
LEFT JOIN (SELECT distinct user_id FROM 用户行为表 WHERE date_str = '2016 - 11 - 01' AND url = ‘B 页面路径’) b ON a.user_id = b.user_id
WHERE b.user_id is not null;
两种方式的差异在于,全量查询后 + where 子句是先进行左连接操作,得到一个临时结果集,然后再对这个结果集进行筛选;而联接前就先做限定是在进行左连接操作之前,先对两个表分别进行筛选,然后再进行连接操作,这样可以减少参与连接操作的数据量,提高查询效率。
(三)在实际项目中的具体案例
在实际项目中,Left Join 有很多实用的场景。比如在一个企业的人力资源管理系统中,有员工表和部门表。要查询所有员工以及他们所属的部门信息,可以使用 Left Join。假设员工表包含员工 ID、员工姓名、部门 ID 等字段,部门表包含部门 ID、部门名称等字段。使用以下 SQL 语句:
SELECT e.员工 ID,e.员工姓名,d.部门名称FROM 员工表 e
LEFT JOIN 部门表 d ON e.部门 ID = d.部门 ID;
这样就可以得到所有员工的姓名以及他们所属的部门名称。即使某些员工没有分配部门,也会在查询结果中显示,部门名称为 null。这个例子体现了 Left Join 在实际项目中的实用性,可以方便地获取多个表中的相关信息,满足不同的业务需求。
相关推荐
- oracle数据导入导出_oracle数据导入导出工具
-
关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...
- 继续学习Python中的while true/break语句
-
上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个...
- python continue和break的区别_python中break语句和continue语句的区别
-
python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...
- 简单学Python——关键字6——break和continue
-
Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
-
用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...
- Python 中 break 和 continue 傻傻分不清
-
大家好啊,我是大田。...
- python中的流程控制语句:continue、break 和 return使用方法
-
Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...
- L017:continue和break - 教程文案
-
continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...
- 作为前端开发者,你都经历过怎样的面试?
-
已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...
- 面试被问 const 是否不可变?这样回答才显功底
-
作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...
- 2023金九银十必看前端面试题!2w字精品!
-
导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。...
- 前端面试总结_前端面试题整理
-
记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...
- 由浅入深,66条JavaScript面试知识点(七)
-
作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录...
- 2024前端面试真题之—VUE篇_前端面试题vue2020及答案
-
添加图片注释,不超过140字(可选)...
- 今年最常见的前端面试题,你会做几道?
-
在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...
- 一周热门
- 最近发表
-
- oracle数据导入导出_oracle数据导入导出工具
- 继续学习Python中的while true/break语句
- python continue和break的区别_python中break语句和continue语句的区别
- 简单学Python——关键字6——break和continue
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
- Python 中 break 和 continue 傻傻分不清
- python中的流程控制语句:continue、break 和 return使用方法
- L017:continue和break - 教程文案
- 作为前端开发者,你都经历过怎样的面试?
- 面试被问 const 是否不可变?这样回答才显功底
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)
