一、数据
首先我给出我的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会帮我们删除)
八、总结
到这里也就结束了,基本上就这些内容了,其实你会觉得多表查询并不难,确实,你看我写当然不难啦,在实际的需求当中,往往要复杂得多,唯一的办法就是不断不断地进行练习,反复地进行练习,不断总结,才能熟能生巧。【这段是废话】
这篇写了几千字,写了几个小时,实属不易,然后就是后面还会更新这篇文章,就是会加上一些针对上面的表的练习,让你对连接查询更加熟练,喜欢的话就点点收藏喽。
☞ 最后求个赞 √