您当前的位置:首页 > 计算机 > 软件应用 > 数据库 > MySQL

这可能是最容易理解的有关MySQL连接查询的文章,内连接,左连接,右连接和外连接

时间:07-05来源:作者:点击数:

一、数据

首先我给出我的sql,大家可以直接复制粘贴,以方便自己测试这几种连接

DROP TABLE IF EXISTS EMP;
DROP TABLE IF EXISTS DEPT;
DROP TABLE IF EXISTS SALGRADE;

CREATE TABLE DEPT(
	DEPTNO INT(2) NOT NULL ,
	DNAME VARCHAR(14) ,
	LOC VARCHAR(13),
	PRIMARY KEY (DEPTNO)
);
CREATE TABLE EMP(
	EMPNO INT(4)  NOT NULL ,
	ENAME VARCHAR(10),
	JOB VARCHAR(9),
	MGR INT(4),
	HIREDATE DATE  DEFAULT NULL,
	SAL DOUBLE(7,2),
	COMM DOUBLE(7,2),
	PRIMARY KEY (EMPNO),
	DEPTNO INT(2) 
);
CREATE TABLE SALGRADE(
	GRADE INT,
	LOSAL INT,
	HISAL INT 
);

INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES 
(1, '财务部', '北京'),
(2, '研发部', '上海'), 
(3, '销售部', '深圳'),
(4, '管理层', '广州'); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO ) VALUES 
( 1001, '张三', '文员', 1004,  '1980-12-17', 800, NULL, 1),
( 1002, '李四', '销售员', 1006,  '1981-02-20', 1600, 300, 3),
( 1003, '王五', '销售员', 1006,  '1981-02-22', 1250, 500, 3),
( 1004, '赵六', '经理', 1009,  '1981-04-02', 2975, NULL, 2),
( 1005, '皮卡丘', '销售员', 1006,  '1981-09-28', 1250, 1400, 3), 
( 1006, '小火龙', '经理', 1009,  '1981-05-01', 2850, NULL, 3),
( 1007, '妙蛙草', '经理', 1009,  '1981-06-09', 2450, NULL, 1),
( 1008, '杰尼龟', '分析师', 1007,  '1987-04-19', 3000, NULL, 2),
( 1009, '刘备', '董事长', NULL,  '1981-11-17', 5000, NULL, 4),
( 1010, '关羽', '销售员', 1006,  '1981-09-08', 1500, 0, 3),
( 1011, '张飞', '文员', 1007,  '1987-05-23', 1100, NULL, 2), 
( 1012, '钢铁侠', '文员', 1006,  '1981-12-03', 950, NULL, 3),
( 1013, '绿巨人', '分析师', 1007,  '1981-12-03', 3000, NULL, 2),
( 1014, '雷神', '文员', 1004,  '1982-01-23', 1300, NULL, 1);
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES 
( 1, 700, 1200),
( 2, 1201, 1400),
( 3, 1401, 2000),
( 4, 2001, 3000),
( 5, 3001, 9999); 

顺便查询一下各个表

部门表:

员工表:

工资等级表:


二、笛卡尔积

什么是笛卡尔积呢?

可以理解成,假如A表有3条记录,B表有2条记录,那么联合两个表查询之后会有 2x3 条记录;【我们所做的各种连接查询,就是在笛卡尔积后得到的 2x3 条记录的表进行筛选,把我们不想要的东西去掉。

下文我直接称它为【笛卡尔积表】。

如下图:当我们执行sql语句,select * from student,teacher; 的时候,发现共6条记录

下图中,显然不是我们想要的结果,我们想要的,应该是需要 TEANO=TNO 的那部分记录,因为这样才能一一对应学生和老师。

对于这个笛卡尔积表,我们很容易想到用 where 条件查询来进行筛选,即

select
	*
from
	student s,teacher t
where
	s.teano = t.tno;

得到

有个问题:

既然我们能够用 where 就得到我们想要的结果,那还要用 join on 做什么,那还要学连接查询做什么?

诶,这个问题问得好,在我们的 sql92(老版本)中,我们确实是用 where 来实现多表查询;但是到了 sql99 ,就开始使用 join on 了,那用 where 不好吗?其实确实是一样的,只不过用 where 能代替的,仅仅只是内连接啊,其他的连接方式 where 不就实现不了了吗?

还有,以下是我的三点见解:

1)join on 的效率比 where 的效率更高。【不要问我为什么,我也不知道】

2)从代码(见内连接部分)可以看出,where 的连接和过滤条件放在了一起,结构不清晰;join on 则是实现了连接和过滤的解耦。

3)使用 join on 的话,我们对查询出来的结果,还能使用 where 再进行过滤,就很快乐 (*^▽^*)

笛卡尔积这部分

我只是为了引出多表查询时,如果不加条件进行过滤的话,会造成大量的数据冗余,且这些冗余的数据不是我们想要的这个事实,让我们知道了学习连接查询的必要性;同时也引出了 join on 关键字,为下文做了铺垫。


三、连接查询

在开始之前,先看看下面几个问题,做初步的了解。

1)什么是连接查询?

在实际开发中,绝大部分的情况下都不是从单表查询数据,一般都是多张表联合起来取出最终的结果。我们联合多个表进行查询,这个过程就叫做连接查询。用join on实现。

2)连接查询有几种形式?

一共有四种,分为内连接、左连接、右连接和外连接。【其实左连接、右连接可以说也是属于外连接的】

(这个图先有个印象就行,后面慢慢剖析)

3)如何看懂上图?

A、B 分别表示两个表,用两个圆来表示两个表中各自所有记录的集合,交叉的部分表示 A、B 两表公共的记录(这里的公共,可以理解成是具有相同意义的字段)

解释了一大堆内容,我们终于可以开始讲“四大天王”:内连接、左连接、右连接、外连接了,我真是太难了!

哎呀,行了行了,不废话了,快上车!

在这里插入图片描述

四、内连接

内连接分为两种:等值连接和非等值连接。【又是整一些花里胡哨的名字】

等值连接

为了对比 where 和 join on,把where的代码也写一下。我们可以得到相同的结果。

select
	e.ename,d.dname
from
	emp e,dept d
where
	e.deptno = d.deptno;
select
	e.ename,d.dname
from
	emp e
inner join  -- inner是可以省略的,但是为了提高代码的可读性,我还是建议加上去
	dept d
on
	e.deptno = d.deptno;

显然,之所以称为等值连接,是因为在笛卡尔积表中,我们需要的记录仅仅是两个同名字段 deptno 的值相同时的记录,因此称为等值,也就是连接条件的关系是等值关系。

非等值连接

非等值连接,自然与等值连接关系对立,其连接条件的关系是非等值关系。(难懂?看代码就完事了)

比如我们要显示员工名,工资,以及对应的工资等级,要知道,工资与工资等级的关系是非等值关系,因为工资等级是根据某个范围进行划分的,而工资是一个确定的值,如何表示呢?

select
	e.ename,e.sal,s.grade
from
	emp e
inner join
	salgrade s
on
	e.sal<s.hisal && e.sal>s.losal;

emmm,关于内连接,看起来很简单,可是我们还是不知道什么是内连接;为什么要称它为内连接。

在这里插入图片描述

其他的博客中,这样解释内连接:能获取多个表的公共资源。

这样解释确实对了,但是如何理解这个 “公共资源” 呢?别急,看看左连接你就懂了。


五、左连接

为了更好地理解左连接,我们先将表中的就修改一下,将几个员工的 deptno 修改为 null,sql语句我帮你们写好了

UPDATE emp SET deptno = NULL WHERE empno = 1001;
UPDATE emp SET deptno = NULL WHERE empno = 1002;
UPDATE emp SET deptno = NULL WHERE empno = 1003;
UPDATE emp SET deptno = NULL WHERE empno = 1004;
COMMIT;

然后执行一下我们上面的内连接代码,即

select
	e.ename,d.dname
from
	emp e
inner join  
	dept d
on
	e.deptno = d.deptno;

和之前的对比,是不是有什么发现呢? 对了,我们发现和之前对比,张三、李四、王五、赵六都消失了。

为什么呢?诶,这就要填我们在内连接中的那个坑了,也就是为什么称为“公共资源”。

在 emp 表中,我们已经将四人的 deptno 修改成了 null,但是在 dept 表中,deptno 并没有修改成 null;也就是这个资源并不是公共的了,在 emp 表中这四人已经没有和 dept 表相匹配的条件了。既然不符合条件,那当然就消失了。【所以,这就是内连接】

在这里插入图片描述

那么,假如即使这四个人没有对应的部门,我们还是想要将这四个人显示出来呢(【这里可以想象成现实生活中的例子,他们这四个人已经被公司招聘了,只是还没有分配好部门,那我们总不能说不显示它们,或者 说他们不在这家公司吧】),答案是将 inner 修改为 left,没错!就这么简单!

select
	e.ename,d.dname
from
	emp e
left join  
	dept d
on
	e.deptno = d.deptno;

这是什么原因呢?让我们看看 “7图”,是不是有所发现?

没错,这个左连接这个【“左”】字就很有灵性,它是说将左边那个表作为主表,即 emp 这个表,无论你右表能不能与我配对,我左表的记录就一定要显示出来,即使这样导致你右表全部都是 null 又如何,我左表就是要显示。

在这里插入图片描述

六、右连接

有了左连接作为基础,右连接就不用多做解释了吧,还是那么简单,不信你看看下面的代码,执行的结果和在左连接中代码的执行结果一模一样。

你一看就秒懂了。

select
	e.ename,d.dname
from
	dept d
right join  
	emp e
on
	e.deptno = d.deptno;

但是在 “7图” 中我们还注意到,左连接和右连接还有另外一种代码形式,就是后面加的 where…is null 的那一坨,如何理解呢?

select
	e.ename,d.dname
from
	dept d
right join  
	emp e
on
	e.deptno = d.deptno
where
	e.deptno is null;

看看结果,在 A 表的所有记录中,与内连接结果相对立的那一个呢?

其实也很好理解, is null 字面理解即可,为空则为真,即能查询出来;不为空则为加,不能被查询出来。


七、外连接

我们先看一下外连接的定义:

假设A和B进行连接,使用外连接的话,A和B两张表中有一张表是主表,一张表示副表,主要查询主表中的数据,捎带着查询副表,当副表中的数据没有和主表中的数据匹配上时,副表自动模拟出null与之匹配,也就是即使主表和副表匹配不上,也要把主表中的数据查出来。

有了前面左连接和右连接作为铺垫进行理解,这段又臭又长的定义是不是就很容易懂了。

这里我又填了前面一个坑,也就是我说过,左连接和外连接其实也属于外连接,就是这么个事。

但是我们在 “7图” 中,貌似只解决了5个,还剩下一个outer,其实它也是外连接的一种,就像左连接和右连接一样。不过它还有自己的名字,就是【全连接】。

从 “7图” 中不难看出,其实全连接是左连接和右连接的一个升级版,就是两个表不再有主副之分,两个表都能模拟出null,两个表都能将自己的所有记录显示出来。

为了试验全连接,我们先将原来的数据修改一下,sql语句如下

ALTER TABLE dept DROP PRIMARY KEY; -- 这一句是将dept表中的主键约束删除
ALTER TABLE dept MODIFY deptno INT(6) NULL; -- 这一句注释将dept表中deptno字段的not null删除

-- 然后修改数据
UPDATE dept SET deptno = NULL WHERE deptno = 1;
UPDATE dept SET deptno = NULL WHERE deptno = 2;

执行之后,现在,我们的emp表和dept表的deptno字段就都存在null了,我们先用左连接试试效果

SELECT
	*
FROM
	emp e
LEFT JOIN  
	dept d
ON
	e.deptno = d.deptno;

再试试右连接的效果

SELECT
	*
FROM
	emp e
right JOIN  
	dept d
ON
	e.deptno = d.deptno;

显然,左连接和右连接都有主副之分。

在左连接中,虽然张三、李四、王五、赵六没有部门与之对应,但还是在副表部门表中模拟出null与之匹配

在右连接中,虽然财务部和研发部没有deptno与员工相对应,但还是在副表员工表中模拟出null与之匹配。

到这里我们就能明白,全连接就是顾及两边,都会互相模拟,我们输入一下sql。

SELECT
	*
FROM
	emp e
FULL OUTER JOIN  
	dept d
ON
	e.deptno = d.deptno;

发现报错了!!!

完全不慌,这是因为,现在的MySQL版本已经不支持这一种写法了,现在的写法是

SELECT
	*
FROM
	emp e
LEFT JOIN  
	dept d
ON
	e.deptno = d.deptno
UNION
SELECT
	*
FROM
	emp e
RIGHT JOIN  
	dept d
ON
	e.deptno = d.deptno;

在上面的代码中,我们发现了一个新的关键字【union】

解释一下这段代码:这里的 union ,其实是将左连接和右连接两种连接得到的结果(也就是两个表),进行了合并,并且会将重复的记录删除。(不信你看,左连接和右连接查询的结果中,是有重复的记录的,union会帮我们删除)


八、总结

到这里也就结束了,基本上就这些内容了,其实你会觉得多表查询并不难,确实,你看我写当然不难啦,在实际的需求当中,往往要复杂得多,唯一的办法就是不断不断地进行练习,反复地进行练习,不断总结,才能熟能生巧。【这段是废话】

这篇写了几千字,写了几个小时,实属不易,然后就是后面还会更新这篇文章,就是会加上一些针对上面的表的练习,让你对连接查询更加熟练,喜欢的话就点点收藏喽。

☞ 最后求个赞 √

在这里插入图片描述
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门