1、连接是关系型数据库的主要特点。连接查询是关系型数据库中最主要的查询,主要包括内连接、外连接等
⑴连接查询:也可以叫跨表查询,需要关联多个表进行查询
2、通过连接运算符可以实现多个表查询
3、在关系数据库管理系统中,表建立时各数据之间的关系不必确定,通常把一个实体的所有信息存放在一个表中
⑴当查询数据时,通过连接操作查询出存放在多个表中的不同实体信息
⑵当两个或多个表中存在相同意义的字段时,便可以通过这些字段对不同的表进行连接查询
注:
1、在实际开发中,如果把所有数据都放在一个表中的话,那么这个表就会显得非常臃肿,不利于表、数据的维护等
2、因此,一般一个业务都会对应多张表,比如:学生和班级,起码两张表
3、这种情况下,大部分的情况都不是从单表中查询数据的,一般都是多张表联合查询取出最终的结果
⑴比如:这里要查询学生班级的信息,那么就会涉及到两张表,需要将两张表以一定的方式关联起来,然后进行查询。这种就是连表查询
4、连接查询总的来说,就是:将多张表连接起来进行查询,最终查询出来的数据可以来自于不同的表
⑴同时查询不同表中的数据
连接查询可以根据连接方式的不同来进行划分,主要分为两类:
1、内连接:
⑴等值连接
⑵非等值连接
⑶自连接
2、外连接:
⑴ 左外连接:又叫做左连接
⑵ 右外连接:又叫做右连接
连接查询基本形式如下:
SELECT ... FROM 表1 [连接方式] JOIN 表2 [ON 连接条件] WHERE 过滤条件
注:
1、可见,连接查询只是作为from子句的“数据源”
2、或者说,连接查询是扩大了数据源,从原来的一个表作为数据源,扩大为多个表作为数据源
3、筛选的顺序on优先于where
⑴ON关键字表示:两张表的连接条件(外键关联), where会把前面的结果集进行筛选(可以筛选所有字段条件)
1、连接查询,是将两个表的每一行,以“两两横向对接”的方式,所得到的所有行的结果
⑴即一个表中的某行,跟另一个表中的某行,进行“横向对接”,而得到一个新行
2、整体连接结构如下图所示
注:
1、可见,假设:表1有n1行,m1列。表2有n2行,m2列
⑴则表1和表2“连接”之后,就会有:n1*n2行,m1+m2列
2、如果只是简单的将两个或多个表连接起来的话(无连接条件),就会造成一种笛卡尔积现象
⑴就是:不管两个表中的两行有无关系,都会两两连接起来(n1*n2行)
3、笛卡尔积现象:当两张表进行连接查询的时候,没有任何条件进行限制,最终的查询结果条数是两张表记录条数的乘积
例1:查询每一个员工的部门名称,要求显示员工名和部门名
mysql> SELECT * FROM dept;
+----+------------+
| id | name |
+----+------------+
| 1 | 人力资源部 |
| 2 | 财务部 |
| 3 | 后勤部 |
| 4 | 法务部 |
+----+------------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM emp;
+----+------+---------+
| id | name | dept_id |
+----+------+---------+
| 1 | 刘备 | 3 |
| 2 | 关羽 | 3 |
| 3 | 张飞 | 3 |
| 4 | 张角 | 2 |
| 5 | 张宝 | 2 |
| 6 | 张梁 | 2 |
| 7 | 曹操 | 1 |
| 8 | 曹丕 | 1 |
| 9 | 曹植 | 1 |
| 10 | 曹冲 | 1 |
| 11 | 孙权 | 0 |
+----+------+---------+
11 rows in set (0.00 sec)
mysql> SELECT e.name,d.name FROM emp e JOIN dept d;
+------+------------+
| name | name |
+------+------------+
| 刘备 | 人力资源部 |
| 刘备 | 财务部 |
| 刘备 | 后勤部 |
| 刘备 | 法务部 |
| 关羽 | 人力资源部 |
| 关羽 | 财务部 |
| 关羽 | 后勤部 |
| 关羽 | 法务部 |
| 张飞 | 人力资源部 |
| 张飞 | 财务部 |
| 张飞 | 后勤部 |
| 张飞 | 法务部 |
| 张角 | 人力资源部 |
| 张角 | 财务部 |
| 张角 | 后勤部 |
| 张角 | 法务部 |
| 张宝 | 人力资源部 |
| 张宝 | 财务部 |
| 张宝 | 后勤部 |
| 张宝 | 法务部 |
| 张梁 | 人力资源部 |
| 张梁 | 财务部 |
| 张梁 | 后勤部 |
| 张梁 | 法务部 |
| 曹操 | 人力资源部 |
| 曹操 | 财务部 |
| 曹操 | 后勤部 |
| 曹操 | 法务部 |
| 曹丕 | 人力资源部 |
| 曹丕 | 财务部 |
| 曹丕 | 后勤部 |
| 曹丕 | 法务部 |
| 曹植 | 人力资源部 |
| 曹植 | 财务部 |
| 曹植 | 后勤部 |
| 曹植 | 法务部 |
| 曹冲 | 人力资源部 |
| 曹冲 | 财务部 |
| 曹冲 | 后勤部 |
| 曹冲 | 法务部 |
| 孙权 | 人力资源部 |
| 孙权 | 财务部 |
| 孙权 | 后勤部 |
| 孙权 | 法务部 |
+------+------------+
44 rows in set (0.00 sec)
注:
1、首先确定最终查询的数据:查询每一个员工的部门名称,要求显示员工名和部门名
⑴员工名来自于emp表
⑵部门名来自于dept表
⑶最终查询的数据来自于两张表,因此需要使用连接查询
2、从上面的数据结果可以看出:员工表中的一个员工都会和部门表中的每一个部门拼接一次,因此最后拼接的数据行数为11*4=44
⑴造成上面这个现象的原因就是:在连接表时,没有指名连接的条件
⑵这种现象就叫做:笛卡尔积现象
3、连接查询的原理是:A表与B表连接的时候,依次将A表中的每一行数据拿来与B表中的所有数据行都进行匹配
⑴将A表中的每一行数据都拿来匹配拼接到B表中的每一行后(可以把拼接后的数据看做一个新表,只是实际不存在)
4、因此在进行表的连接时,一定要加上连接条件进行过滤
5、还有一点是:这个例子中在使用表名和列名时都加上了对应的别名,这样做的话能很清楚的明白该字段属于哪张表(建议这么做)
例2:避免笛卡尔积现象
mysql> SELECT * FROM dept;
+----+------------+
| id | name |
+----+------------+
| 1 | 人力资源部 |
| 2 | 财务部 |
| 3 | 后勤部 |
| 4 | 法务部 |
+----+------------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM emp;
+----+------+---------+
| id | name | dept_id |
+----+------+---------+
| 1 | 刘备 | 3 |
| 2 | 关羽 | 3 |
| 3 | 张飞 | 3 |
| 4 | 张角 | 2 |
| 5 | 张宝 | 2 |
| 6 | 张梁 | 2 |
| 7 | 曹操 | 1 |
| 8 | 曹丕 | 1 |
| 9 | 曹植 | 1 |
| 10 | 曹冲 | 1 |
| 11 | 孙权 | 0 |
+----+------+---------+
11 rows in set (0.00 sec)
-- 这里是返回两个表中的所有字段
mysql> SELECT * FROM dept d JOIN emp e ON e.dept_id=d.id;
+----+------------+----+------+---------+
| id | name | id | name | dept_id |
+----+------------+----+------+---------+
| 3 | 后勤部 | 1 | 刘备 | 3 |
| 3 | 后勤部 | 2 | 关羽 | 3 |
| 3 | 后勤部 | 3 | 张飞 | 3 |
| 2 | 财务部 | 4 | 张角 | 2 |
| 2 | 财务部 | 5 | 张宝 | 2 |
| 2 | 财务部 | 6 | 张梁 | 2 |
| 1 | 人力资源部 | 7 | 曹操 | 1 |
| 1 | 人力资源部 | 8 | 曹丕 | 1 |
| 1 | 人力资源部 | 9 | 曹植 | 1 |
| 1 | 人力资源部 | 10 | 曹冲 | 1 |
+----+------------+----+------+---------+
10 rows in set (0.00 sec)
注:上面例子中
1、增加了两个表之间的连接条件"e.dept_id=d.id"(ON关键字之后)
2、表示:员工表在与部门表进行匹配拼接的时候,需要员工表中的dept_id字段值等于部门表中的id字段值时才进行匹配拼接
⑴如果:dept_id字段值不等于id字段值时,也会进行匹配,但是不会拼接返回
3、避免了笛卡尔积现象,但是并不会减少数据行的匹配次数,只是减少返回的数据行
⑴比如:上面例子中,"刘备"的部门为3,在进行连接匹配的时候,"刘备"依旧会与所有部门进行匹配,但是只会返回部门id为3的数据行
1、内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行,组合成新的数据行
⑴也就是说:在内连接查询中,只有满足条件的记录才能出现在结果关系中
2、语法形式:from 表1 [inner] join 表2 on 连接条件 where 过滤条件
⑴内连接通过on条件来筛选出来符合连接条件的数据
⑵关键字“inner”可以省略,但建议写上
3、内连接是应用最广泛的一种连接查询,其本质是根据条件筛选出“有意义的数据”
1、内连接中的等值连接最大的特点是:条件是等量关系
2、等值连接使用的是"="操作符,表示相等关系
例3:
mysql> SELECT * FROM dept d INNER JOIN emp e ON e.dept_id=d.id;
+----+------------+----+------+---------+
| id | name | id | name | dept_id |
+----+------------+----+------+---------+
| 3 | 后勤部 | 1 | 刘备 | 3 |
| 3 | 后勤部 | 2 | 关羽 | 3 |
| 3 | 后勤部 | 3 | 张飞 | 3 |
| 2 | 财务部 | 4 | 张角 | 2 |
| 2 | 财务部 | 5 | 张宝 | 2 |
| 2 | 财务部 | 6 | 张梁 | 2 |
| 1 | 人力资源部 | 7 | 曹操 | 1 |
| 1 | 人力资源部 | 8 | 曹丕 | 1 |
| 1 | 人力资源部 | 9 | 曹植 | 1 |
| 1 | 人力资源部 | 10 | 曹冲 | 1 |
+----+------------+----+------+---------+
10 rows in set (0.00 sec)
-- 查询部门id大于2的员工、部门信息
mysql> SELECT d.id,e.name,d.name FROM dept d INNER JOIN emp e ON e.dept_id=d.id WHERE d.id > 2;
+----+------+--------+
| id | name | name |
+----+------+--------+
| 3 | 刘备 | 后勤部 |
| 3 | 关羽 | 后勤部 |
| 3 | 张飞 | 后勤部 |
+----+------+--------+
3 rows in set (0.00 sec)
注:
1、在查询"查询部门id大于2的员工、部门信息"的SQL语句中,可以将其看做两步
⑴先查询出员工、部门信息,类似于这个例子中的" SELECT * FROM dept d INNER JOIN emp e ON e.dept_id=d.id;"
⑵此时就可以将步骤1中的结果想象成一个表,只是这个表实际不存在
⑶然后在步骤2中的这个想象表中过滤查询部门id大于2的数据(WHERE关键字在ON关键字之后)
⑷自己感觉有一个比较重要的思想:将查询结果看做一张临时表,然后继续在这个临时表中过滤查询数据、这张临时表继续关联其他表等
2、在SQL语句中还可以在SELECT关键字之后指定返回的数据列:数据列可以来自不同的表
⑴在指定列名时,注意加上列的别名
1、内连接中的非等值连接最大的特点是:条件是非等量关系
2、非等值连接使用的是BETWEEN...AND...关键字,表示在某个范围内
例4:查询员工工资等级
mysql> SELECT * FROM emp;
+----+------+---------+------+
| id | name | dept_id | sal |
+----+------+---------+------+
| 1 | 刘备 | 3 | 2975 |
| 2 | 关羽 | 3 | 1250 |
| 3 | 张飞 | 3 | 2850 |
| 4 | 张角 | 2 | 2450 |
| 5 | 张宝 | 2 | 800 |
| 6 | 张梁 | 2 | 5000 |
| 7 | 曹操 | 1 | 710 |
| 8 | 曹丕 | 1 | 3000 |
| 9 | 曹植 | 1 | 1300 |
| 10 | 曹冲 | 1 | 950 |
| 11 | 孙权 | 0 | 2000 |
+----+------+---------+------+
11 rows in set (0.00 sec)
mysql> SELECT * FROM salgrade;
+----+-------+-------+
| id | losat | hisat |
+----+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 99999 |
+----+-------+-------+
5 rows in set (0.00 sec)
mysql> SELECT * FROM emp e INNER JOIN salgrade s ON e.sal BETWEEN s.losat AND s.hisat;
+----+------+---------+------+----+-------+-------+
| id | name | dept_id | sal | id | losat | hisat |
+----+------+---------+------+----+-------+-------+
| 1 | 刘备 | 3 | 2975 | 4 | 2001 | 3000 |
| 2 | 关羽 | 3 | 1250 | 2 | 1201 | 1400 |
| 3 | 张飞 | 3 | 2850 | 4 | 2001 | 3000 |
| 4 | 张角 | 2 | 2450 | 4 | 2001 | 3000 |
| 5 | 张宝 | 2 | 800 | 1 | 700 | 1200 |
| 6 | 张梁 | 2 | 5000 | 5 | 3001 | 99999 |
| 7 | 曹操 | 1 | 710 | 1 | 700 | 1200 |
| 8 | 曹丕 | 1 | 3000 | 4 | 2001 | 3000 |
| 9 | 曹植 | 1 | 1300 | 2 | 1201 | 1400 |
| 10 | 曹冲 | 1 | 950 | 1 | 700 | 1200 |
| 11 | 孙权 | 0 | 2000 | 3 | 1401 | 2000 |
+----+------+---------+------+----+-------+-------+
11 rows in set (0.00 sec)
例5:
mysql> SELECT e.name, e.sal, s.id FROM emp e INNER JOIN salgrade s ON e.sal BETWEEN s.losat AND s.hisat;
+------+------+----+
| name | sal | id |
+------+------+----+
| 刘备 | 2975 | 4 |
| 关羽 | 1250 | 2 |
| 张飞 | 2850 | 4 |
| 张角 | 2450 | 4 |
| 张宝 | 800 | 1 |
| 张梁 | 5000 | 5 |
| 曹操 | 710 | 1 |
| 曹丕 | 3000 | 4 |
| 曹植 | 1300 | 2 |
| 曹冲 | 950 | 1 |
| 孙权 | 2000 | 3 |
+------+------+----+
11 rows in set (0.00 sec)
-- 查询员工工资大于3000的工资等级
mysql> SELECT e.name, e.sal, s.id FROM emp e INNER JOIN salgrade s ON e.sal BETWEEN s.losat AND s.hisat WHERE e.sal > 3000;
+------+------+----+
| name | sal | id |
+------+------+----+
| 张梁 | 5000 | 5 |
+------+------+----+
1 row in set (0.00 sec)
1、自连接最大的特点是:一张表看做两张表,自己连接自己
2、自连接其实还是两个表连接,只是将一个表用不同的别名,当做两个表
3、自连接适用于一个表中的某个字段的值“来源于”当前表的另一个字段的情况
4、语法为:from 表1 as a [连接形式] join 表1 as b on a.xx字段1=b.xx字段名
例6:查询每个员工的上级领导
mysql> SELECT * FROM emp;
+----+------+---------+------+-----------+
| id | name | dept_id | sal | leader_id |
+----+------+---------+------+-----------+
| 1 | 刘备 | 3 | 2975 | 2 |
| 2 | 关羽 | 3 | 1250 | 1 |
| 3 | 张飞 | 3 | 2850 | 1 |
| 4 | 张角 | 2 | 2450 | 3 |
| 5 | 张宝 | 2 | 800 | 2 |
| 6 | 张梁 | 2 | 5000 | 1 |
| 7 | 曹操 | 1 | 710 | 7 |
| 8 | 曹丕 | 1 | 3000 | 4 |
| 9 | 曹植 | 1 | 1300 | 11 |
| 10 | 曹冲 | 1 | 950 | 5 |
| 11 | 孙权 | 0 | 2000 | 1 |
+----+------+---------+------+-----------+
11 rows in set (0.00 sec)
mysql> SELECT e1.name,e2.name FROM emp e1 INNER JOIN emp e2 ON e1.leader_id = e2.id;
+------+------+
| name | name |
+------+------+
| 刘备 | 关羽 |
| 关羽 | 刘备 |
| 张飞 | 刘备 |
| 张角 | 张飞 |
| 张宝 | 关羽 |
| 张梁 | 刘备 |
| 曹操 | 曹操 |
| 曹丕 | 张角 |
| 曹植 | 孙权 |
| 曹冲 | 张宝 |
| 孙权 | 刘备 |
+------+------+
11 rows in set (0.00 sec)
mysql> SELECT * FROM emp e1 INNER JOIN emp e2 ON e1.leader_id = e2.id;
+----+------+---------+------+-----------+----+------+---------+------+-----------+
| id | name | dept_id | sal | leader_id | id | name | dept_id | sal | leader_id |
+----+------+---------+------+-----------+----+------+---------+------+-----------+
| 1 | 刘备 | 3 | 2975 | 2 | 2 | 关羽 | 3 | 1250 | 1 |
| 2 | 关羽 | 3 | 1250 | 1 | 1 | 刘备 | 3 | 2975 | 2 |
| 3 | 张飞 | 3 | 2850 | 1 | 1 | 刘备 | 3 | 2975 | 2 |
| 4 | 张角 | 2 | 2450 | 3 | 3 | 张飞 | 3 | 2850 | 1 |
| 5 | 张宝 | 2 | 800 | 2 | 2 | 关羽 | 3 | 1250 | 1 |
| 6 | 张梁 | 2 | 5000 | 1 | 1 | 刘备 | 3 | 2975 | 2 |
| 7 | 曹操 | 1 | 710 | 7 | 7 | 曹操 | 1 | 710 | 7 |
| 8 | 曹丕 | 1 | 3000 | 4 | 4 | 张角 | 2 | 2450 | 3 |
| 9 | 曹植 | 1 | 1300 | 11 | 11 | 孙权 | 0 | 2000 | 1 |
| 10 | 曹冲 | 1 | 950 | 5 | 5 | 张宝 | 2 | 800 | 2 |
| 11 | 孙权 | 0 | 2000 | 1 | 1 | 刘备 | 3 | 2975 | 2 |
+----+------+---------+------+-----------+----+------+---------+------+-----------+
11 rows in set (0.00 sec)
注:
1、在员工表中:同时记录的员工和其领导的id(有些既是员工又是领导)
2、此时就需要将一张表看做两张表:一张单纯的员工表e1,一张单纯的领导表e2,两个表通过dept_id和id关联
⑴因此:关联的条件为"员工的领导编号=领导的员工编号"
3、这个例子中就充分体现出了给表取别名的重要性了
⑴如果没有给表取别名,那么在查询时数据库都不知道leader_id和id是属于那张表的
1、在内连接查询时,返回的查询结果集中仅是符合查询条件和连接条件的行
2、但是有时候需要包含没有关联的行中的数据,即返回的查询结果集中不仅需要包含符合连接条件的行,还需要包含左表(左外连接或左连接)、右表(右外连接或右连接)或两个连接表(全外连接)中的所有行
3、外连接分为左外连接和右外连接
⑴LEFT JOIN(左外连接):返回包括左表中的所有记录和右表中连接字段相等的记录
⑵RIGHT JOIN(右外连接):返回包括右表中的所有记录和左表中连接字段相等的记录
1、内连接:
⑴假设A表和B表进行连接,使用内连接的话,凡是A表和B表能够匹配上的记录都将被查询出来
⑵A、B两张表之间没有主副之分,两张表是平等的
⑶也就是:A、B两张表中的数据能匹配上的话(符合连接条件),那么就查询出来。匹配不上的话,那么就不查询出来
2、外连接
⑴假设A表和B表进行连接,使用外连接的话,A、B两张表中有一张表是主表,一张表是副表,主要查询的是主表中的数据,捎带着查询副表中的数据
⑵当副表中的数据没有和主表中的数据匹配上时,副表将自动模拟出NULL与之匹配
⑶也就是:不管符合不符合连接条件,主表中的所有数据一定会查询出来。副表中的数据如果能匹配,那么就返回匹配的数据;不能匹配的话,就用一个NULL来代替匹配
1、左外连接又叫做左连接:表示左边这张表是主表
⑴left关键字之前的表为主表(左边的表为主表)
2、左连接的结果包括LEFT OUTER子句中指定的左表的所有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配的行,那么右表将使用空值来与之匹配
3、左连接有右连接的写法,右连接也有对应的左连接的写法
4、语法形式:from 表1 left [outer] join 表2 on 连接条件 where 过滤条件
⑴左外连接其实是保证左边表的数据都能够取出的一种连接
⑵左外连接其实是在内连接的基础上,再加上左边表中所有不能满足条件的数据
⑶关键字“outer”可以省略
例7:查询所有员工的部门
mysql> SELECT * FROM emp;
+----+------+---------+------+-----------+
| id | name | dept_id | sal | leader_id |
+----+------+---------+------+-----------+
| 1 | 刘备 | 3 | 2975 | 2 |
| 2 | 关羽 | 3 | 1250 | 1 |
| 3 | 张飞 | 3 | 2850 | 1 |
| 4 | 张角 | 2 | 2450 | 3 |
| 5 | 张宝 | 2 | 800 | 2 |
| 6 | 张梁 | 2 | 5000 | 1 |
| 7 | 曹操 | 1 | 710 | 7 |
| 8 | 曹丕 | 1 | 3000 | 4 |
| 9 | 曹植 | 1 | 1300 | 11 |
| 10 | 曹冲 | 1 | 950 | 5 |
| 11 | 孙权 | 0 | 2000 | 1 |
+----+------+---------+------+-----------+
11 rows in set (0.00 sec)
mysql> SELECT * FROM dept;
+----+------------+
| id | name |
+----+------------+
| 1 | 人力资源部 |
| 2 | 财务部 |
| 3 | 后勤部 |
| 4 | 法务部 |
| 5 | 开发部 |
+----+------------+
5 rows in set (0.00 sec)
mysql> SELECT * FROM emp e LEFT JOIN dept d ON e.dept_id = d.id;
+----+------+---------+------+-----------+------+------------+
| id | name | dept_id | sal | leader_id | id | name |
+----+------+---------+------+-----------+------+------------+
| 7 | 曹操 | 1 | 710 | 7 | 1 | 人力资源部 |
| 8 | 曹丕 | 1 | 3000 | 4 | 1 | 人力资源部 |
| 9 | 曹植 | 1 | 1300 | 11 | 1 | 人力资源部 |
| 10 | 曹冲 | 1 | 950 | 5 | 1 | 人力资源部 |
| 4 | 张角 | 2 | 2450 | 3 | 2 | 财务部 |
| 5 | 张宝 | 2 | 800 | 2 | 2 | 财务部 |
| 6 | 张梁 | 2 | 5000 | 1 | 2 | 财务部 |
| 1 | 刘备 | 3 | 2975 | 2 | 3 | 后勤部 |
| 2 | 关羽 | 3 | 1250 | 1 | 3 | 后勤部 |
| 3 | 张飞 | 3 | 2850 | 1 | 3 | 后勤部 |
| 11 | 孙权 | 0 | 2000 | 1 | NULL | NULL |
+----+------+---------+------+-----------+------+------------+
11 rows in set (0.00 sec)
例7_1:
mysql> SELECT e.id AS id,e.name AS NAME,e.sal AS sal,d.name AS dept_name FROM emp e LEFT JOIN dept d ON e.dept_id = d.id;
+----+------+------+------------+
| id | NAME | sal | dept_name |
+----+------+------+------------+
| 7 | 曹操 | 710 | 人力资源部 |
| 8 | 曹丕 | 3000 | 人力资源部 |
| 9 | 曹植 | 1300 | 人力资源部 |
| 10 | 曹冲 | 950 | 人力资源部 |
| 4 | 张角 | 2450 | 财务部 |
| 5 | 张宝 | 800 | 财务部 |
| 6 | 张梁 | 5000 | 财务部 |
| 1 | 刘备 | 2975 | 后勤部 |
| 2 | 关羽 | 1250 | 后勤部 |
| 3 | 张飞 | 2850 | 后勤部 |
| 11 | 孙权 | 2000 | NULL |
+----+------+------+------------+
11 rows in set (0.00 sec)
注:
1、使用左(外)连接时,LEFT关键字之前的表为主表(左边的表为主表):主表中的数据会全部查询出来
⑴主表中的数据在副表中没有与之匹配的,那么将使用NULL来代替
⑵副表中的数据在主表中没有匹配的数据则不会被返回
2、可以看到员工"孙权"在部门表中没有与之匹配的数据(其无部门信息),但是依旧被查询出来了,且其部门信息使用NULL代替了
⑴部门id为5的数据在副表中且在主表中没有与之匹配的数据,则该条数据不会被返回
3、在SQL语句中还可以在SELECT关键字之后指定返回的数据列:数据列可以来自不同的表
⑴在指定列名时,注意加上列的别名
1、右外连接又叫做右连接:表示右边这张表是主表
⑴right关键字之后的表为主表(右边的表为主表)
2、右连接是左连接的反向连接,将返回右表的所有行。如果右表的某行在左表中没有匹配行,左表将返回空值
3、左连接有右连接的写法,右连接也有对应的左连接的写法
4、语法形式:from 表1 right [outer] join 表2 on 连接条件 where 过滤条件
⑴右外连接其实是保证右边表的数据都能够取出的一种连接
⑵右外连接其实是在内连接的基础上,再加上右边表中所有不能满足条件的数据
⑶关键字“outer”可以省略
例8:
mysql> SELECT * FROM emp;
+----+------+---------+------+-----------+
| id | name | dept_id | sal | leader_id |
+----+------+---------+------+-----------+
| 1 | 刘备 | 3 | 2975 | 2 |
| 2 | 关羽 | 3 | 1250 | 1 |
| 3 | 张飞 | 3 | 2850 | 1 |
| 4 | 张角 | 2 | 2450 | 3 |
| 5 | 张宝 | 2 | 800 | 2 |
| 6 | 张梁 | 2 | 5000 | 1 |
| 7 | 曹操 | 1 | 710 | 7 |
| 8 | 曹丕 | 1 | 3000 | 4 |
| 9 | 曹植 | 1 | 1300 | 11 |
| 10 | 曹冲 | 1 | 950 | 5 |
| 11 | 孙权 | 0 | 2000 | 1 |
+----+------+---------+------+-----------+
11 rows in set (0.00 sec)
mysql> SELECT * FROM dept;
+----+------------+
| id | name |
+----+------------+
| 1 | 人力资源部 |
| 2 | 财务部 |
| 3 | 后勤部 |
| 4 | 法务部 |
| 5 | 开发部 |
+----+------------+
5 rows in set (0.00 sec)
mysql> SELECT * FROM dept d RIGHT JOIN emp e ON d.id=e.dept_id;
+------+------------+----+------+---------+------+-----------+
| id | name | id | name | dept_id | sal | leader_id |
+------+------------+----+------+---------+------+-----------+
| 1 | 人力资源部 | 7 | 曹操 | 1 | 710 | 7 |
| 1 | 人力资源部 | 8 | 曹丕 | 1 | 3000 | 4 |
| 1 | 人力资源部 | 9 | 曹植 | 1 | 1300 | 11 |
| 1 | 人力资源部 | 10 | 曹冲 | 1 | 950 | 5 |
| 2 | 财务部 | 4 | 张角 | 2 | 2450 | 3 |
| 2 | 财务部 | 5 | 张宝 | 2 | 800 | 2 |
| 2 | 财务部 | 6 | 张梁 | 2 | 5000 | 1 |
| 3 | 后勤部 | 1 | 刘备 | 3 | 2975 | 2 |
| 3 | 后勤部 | 2 | 关羽 | 3 | 1250 | 1 |
| 3 | 后勤部 | 3 | 张飞 | 3 | 2850 | 1 |
| NULL | NULL | 11 | 孙权 | 0 | 2000 | 1 |
+------+------------+----+------+---------+------+-----------+
11 rows in set (0.00 sec)
1、三张表以上的连接查询的基础语法:SELECT * FROM A JOIN B JOIN C ON...
⑴其表示:A表和B表先进行表连接,连接之后A表继续和C表进行连接
⑵也可以理解为:A表和B表先进行表连接,连接之后的结果继续与C表进行连接
2、关于连表查询可以有不同的写法,这里介绍两种
1、第一种写法就是前面介绍的:SELECT * FROM A JOIN B JOIN C ON...
2、这种写法我自己叫它链式查询:所有的表都是一次性关联完的
例9:查询每一个员工的部门名称以及工资等级
mysql> SELECT * FROM emp;
+----+------+---------+------+-----------+
| id | name | dept_id | sal | leader_id |
+----+------+---------+------+-----------+
| 1 | 刘备 | 3 | 2975 | 2 |
| 2 | 关羽 | 3 | 1250 | 1 |
| 3 | 张飞 | 3 | 2850 | 1 |
| 4 | 张角 | 2 | 2450 | 3 |
| 5 | 张宝 | 2 | 800 | 2 |
| 6 | 张梁 | 2 | 5000 | 1 |
| 7 | 曹操 | 1 | 710 | 7 |
| 8 | 曹丕 | 1 | 3000 | 4 |
| 9 | 曹植 | 1 | 1300 | 11 |
| 10 | 曹冲 | 1 | 950 | 5 |
| 11 | 孙权 | 0 | 2000 | 1 |
+----+------+---------+------+-----------+
11 rows in set (0.00 sec)
mysql> SELECT * FROM dept;
+----+------------+
| id | name |
+----+------------+
| 1 | 人力资源部 |
| 2 | 财务部 |
| 3 | 后勤部 |
| 4 | 法务部 |
| 5 | 开发部 |
+----+------------+
5 rows in set (0.00 sec)
-- 这里emp和dept使用的是内连接,所以"孙权"未返回
mysql> SELECT e.name,d.name,s.id FROM emp e JOIN dept d ON e.dept_id = d.id JOIN salgrade s ON e.sal BETWEEN s.losat AND s.hisat;
+------+------------+----+
| name | name | id |
+------+------------+----+
| 刘备 | 后勤部 | 4 |
| 关羽 | 后勤部 | 2 |
| 张飞 | 后勤部 | 4 |
| 张角 | 财务部 | 4 |
| 张宝 | 财务部 | 1 |
| 张梁 | 财务部 | 5 |
| 曹操 | 人力资源部 | 1 |
| 曹丕 | 人力资源部 | 4 |
| 曹植 | 人力资源部 | 2 |
| 曹冲 | 人力资源部 | 1 |
+------+------------+----+
10 rows in set (0.00 sec)
例9_1:
-- 上面这个例子可以分为两步来思考
-- 先查询员工的部门、工资信息
-- 在查询员工工资等级
mysql> SELECT e.name,d.name,e.sal FROM emp e JOIN dept d ON e.dept_id = d.id;
+------+------------+------+
| name | name | sal |
+------+------------+------+
| 刘备 | 后勤部 | 2975 |
| 关羽 | 后勤部 | 1250 |
| 张飞 | 后勤部 | 2850 |
| 张角 | 财务部 | 2450 |
| 张宝 | 财务部 | 800 |
| 张梁 | 财务部 | 5000 |
| 曹操 | 人力资源部 | 710 |
| 曹丕 | 人力资源部 | 3000 |
| 曹植 | 人力资源部 | 1300 |
| 曹冲 | 人力资源部 | 950 |
+------+------------+------+
10 rows in set (0.00 sec)
-- 这个SQL中关联了salgrade表,因此就可以把s.id添加到返回列中(SELECT关键字后),如果没有关联salgrade 表,那么返回列中肯定不能有salgrade表中的字段
mysql> SELECT e.name,d.name,e.sal,s.id FROM emp e JOIN dept d ON e.dept_id = d.id JOIN salgrade s ON e.sal BETWEEN s.losat AND s.hisat;
+------+------------+------+----+
| name | name | sal | id |
+------+------------+------+----+
| 刘备 | 后勤部 | 2975 | 4 |
| 关羽 | 后勤部 | 1250 | 2 |
| 张飞 | 后勤部 | 2850 | 4 |
| 张角 | 财务部 | 2450 | 4 |
| 张宝 | 财务部 | 800 | 1 |
| 张梁 | 财务部 | 5000 | 5 |
| 曹操 | 人力资源部 | 710 | 1 |
| 曹丕 | 人力资源部 | 3000 | 4 |
| 曹植 | 人力资源部 | 1300 | 2 |
| 曹冲 | 人力资源部 | 950 | 1 |
+------+------------+------+----+
10 rows in set (0.00 sec)
例10:查询价格大于等于120的水果的名字、供货商名字、水果颜色、水果产地
-- 水果表
-- s_id表示该水果供应商id,关联suppliers表
-- f_origind表示该水果产地,关联fruit_info表
-- f_color表示该水果颜色,关联fruit_info表
mysql> SELECT * FROM fruits;
+------+------+------------+---------+----------+---------+
| f_id | s_id | f_name | f_price | f_origin | f_color |
+------+------+------------+---------+----------+---------+
| 1 | 101 | orange | 111 | 1 | 1 |
| 2 | 102 | apple | 120 | 2 | 1 |
| 3 | 103 | melon | 130 | 3 | 2 |
| 4 | 101 | banana | 101 | 1 | 2 |
| 5 | 103 | grape | 150 | 1 | 3 |
| 6 | 101 | huolongguo | 100 | 2 | 2 |
+------+------+------------+---------+----------+---------+
6 rows in set (0.00 sec)
mysql> SELECT * FROM suppliers;
+------+----------------+--------+-------------+
| s_id | s_name | s_city | s_telephone |
+------+----------------+--------+-------------+
| 101 | 西南第一水果商 | 成都 | 9090980 |
| 102 | 华东第一水果商 | 山东 | 12345678911 |
| 103 | 西北第一水果商 | 山西 | 98765432101 |
+------+----------------+--------+-------------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM fruit_info;
+----+---------+----------+
| id | f_color | f_origin |
+----+---------+----------+
| 1 | yellow | 四川 |
| 2 | green | 山东 |
| 3 | red | 山西 |
+----+---------+----------+
3 rows in set (0.00 sec)
--先关联查询供应商信息
mysql> SELECT f.f_name, f.f_price, s.s_name FROM fruits f LEFT JOIN suppliers s ON f.s_id = s.s_id WHERE f.f_price >= 120;
+--------+---------+----------------+
| f_name | f_price | s_name |
+--------+---------+----------------+
| apple | 120 | 华东第一水果商 |
| melon | 130 | 西北第一水果商 |
| grape | 150 | 西北第一水果商 |
+--------+---------+----------------+
3 rows in set (0.00 sec)
--继续关联查询产地信息
mysql> SELECT f.f_name, f.f_price, s.s_name, i1.f_origin
-> FROM fruits f
-> LEFT JOIN suppliers s ON f.s_id = s.s_id
-> LEFT JOIN fruit_info i1 ON i1.id = f.f_origin
-> WHERE f.f_price >= 120;
+--------+---------+----------------+----------+
| f_name | f_price | s_name | f_origin |
+--------+---------+----------------+----------+
| apple | 120 | 华东第一水果商 | 山东 |
| melon | 130 | 西北第一水果商 | 山西 |
| grape | 150 | 西北第一水果商 | 四川 |
+--------+---------+----------------+----------+
3 rows in set (0.00 sec)
--继续关联查询颜色信息
mysql> SELECT f.f_name, f.f_price, s.s_name, i1.f_origin, i2.f_color
-> FROM fruits f
-> LEFT JOIN suppliers s ON f.s_id = s.s_id
-> LEFT JOIN fruit_info i1 ON i1.id = f.f_origin
-> LEFT JOIN fruit_info i2 ON i2.id = f.f_color
-> WHERE f.f_price >= 120;
+--------+---------+----------------+----------+---------+
| f_name | f_price | s_name | f_origin | f_color |
+--------+---------+----------------+----------+---------+
| apple | 120 | 华东第一水果商 | 山东 | yellow |
| melon | 130 | 西北第一水果商 | 山西 | green |
| grape | 150 | 西北第一水果商 | 四川 | red |
+--------+---------+----------------+----------+---------+
3 rows in set (0.00 sec)
注:
1、上面例10中可以看到fruits关联了两次fruit_info表
⑴从目前的我知道的来看,表与表之间每次只能通过一个字段来进行关联
⑵因此,如果一个表通过不同的字段来关联同一张表,那么就需要将被关联的表看做不同的表来关联多次
⑶比如这里,分别两次将fruit_info表看做i1表和i2表,分两次与fruits表关联
2、表与表之间的关联关系有
⑴一张表中的多个字段关联多张表中的某个字段(例9)
①A表中的a字段关联B表中的a1字段
②A表中的c字段关联C表中的c1字段
⑵一张表中的多个字段关联同一张表中的同一个字段(例10中的fruits表与fruit_info表)
①A表中的a1字段关联B表中的b字段
②A表中的a2字段关联B表中的b字段
1、将查询结果看做一张临时表,然后继续在这个临时表中过滤查询数据、这张临时表继续关联其他表等
2、使用临时表时需要给临时表取别名且临时表中的字段名也需要别名
例11:
mysql> SELECT e.name AS e_name,d.name AS d_name,e.sal AS e_sal FROM emp e JOIN dept d ON e.dept_id = d.id;
+--------+------------+-------+
| e_name | d_name | e_sal |
+--------+------------+-------+
| 刘备 | 后勤部 | 2975 |
| 关羽 | 后勤部 | 1250 |
| 张飞 | 后勤部 | 2850 |
| 张角 | 财务部 | 2450 |
| 张宝 | 财务部 | 800 |
| 张梁 | 财务部 | 5000 |
| 曹操 | 人力资源部 | 710 |
| 曹丕 | 人力资源部 | 3000 |
| 曹植 | 人力资源部 | 1300 |
| 曹冲 | 人力资源部 | 950 |
+--------+------------+-------+
10 rows in set (0.00 sec)
-- 这两个SQL是一样的,只是说下面这个SQL给表和查询结果字段取了别名t1,然后从结果(临时表t1)中查询所有字段
mysql> SELECT t1.* FROM (SELECT e.name AS e_name,d.name AS d_name,e.sal AS e_sal FROM emp e JOIN dept d ON e.dept_id = d.id) AS t1;
+--------+------------+-------+
| e_name | d_name | e_sal |
+--------+------------+-------+
| 刘备 | 后勤部 | 2975 |
| 关羽 | 后勤部 | 1250 |
| 张飞 | 后勤部 | 2850 |
| 张角 | 财务部 | 2450 |
| 张宝 | 财务部 | 800 |
| 张梁 | 财务部 | 5000 |
| 曹操 | 人力资源部 | 710 |
| 曹丕 | 人力资源部 | 3000 |
| 曹植 | 人力资源部 | 1300 |
| 曹冲 | 人力资源部 | 950 |
+--------+------------+-------+
10 rows in set (0.00 sec)
-- 上面结果(临时表)继续关联另一张表,返回t1表中的所有字段和s表中的部分字段
-- 需要注意的是t1表关联salgrade 表时,需要用t1表的字段名来关联了,而不能是e表中的字段名
mysql> SELECT t1.*,s.id FROM (SELECT e.name AS e_name,d.name AS d_name,e.sal AS e_sal FROM emp e JOIN dept d ON e.dept_id = d.id) t1 JOIN salgrade s ON t1.e_sal BETWEEN s.losat AND s.hisat;
+--------+------------+-------+----+
| e_name | d_name | e_sal | id |
+--------+------------+-------+----+
| 刘备 | 后勤部 | 2975 | 4 |
| 关羽 | 后勤部 | 1250 | 2 |
| 张飞 | 后勤部 | 2850 | 4 |
| 张角 | 财务部 | 2450 | 4 |
| 张宝 | 财务部 | 800 | 1 |
| 张梁 | 财务部 | 5000 | 5 |
| 曹操 | 人力资源部 | 710 | 1 |
| 曹丕 | 人力资源部 | 3000 | 4 |
| 曹植 | 人力资源部 | 1300 | 2 |
| 曹冲 | 人力资源部 | 950 | 1 |
+--------+------------+-------+----+
10 rows in set (0.00 sec)
例11_1:
mysql> SELECT f.f_name, f.f_price, s.s_name FROM fruits f LEFT JOIN suppliers s ON f.s_id = s.s_id;
+------------+---------+----------------+
| f_name | f_price | s_name |
+------------+---------+----------------+
| orange | 111 | 西南第一水果商 |
| banana | 101 | 西南第一水果商 |
| huolongguo | 100 | 西南第一水果商 |
| apple | 120 | 华东第一水果商 |
| melon | 130 | 西北第一水果商 |
| grape | 150 | 西北第一水果商 |
+------------+---------+----------------+
6 rows in set (0.00 sec)
--将上面SQL的查询结果看做一张临时表t1
mysql> SELECT t1.*,i1.f_origin AS i1_origin FROM(SELECT f.f_name, f.f_price,f.f_origin,f.f_color, s.s_name FROM fruits f LEFT JOIN suppliers s ON f.s_id = s.s_id) t1 LEFT JOIN fruit_info i1 ON i1.id = t1.f_origin;
+------------+---------+----------+---------+----------------+-----------+
| f_name | f_price | f_origin | f_color | s_name | i1_origin |
+------------+---------+----------+---------+----------------+-----------+
| orange | 111 | 1 | 1 | 西南第一水果商 | 四川 |
| banana | 101 | 1 | 2 | 西南第一水果商 | 四川 |
| huolongguo | 100 | 2 | 2 | 西南第一水果商 | 山东 |
| apple | 120 | 2 | 1 | 华东第一水果商 | 山东 |
| melon | 130 | 3 | 2 | 西北第一水果商 | 山西 |
| grape | 150 | 1 | 3 | 西北第一水果商 | 四川 |
+------------+---------+----------+---------+----------------+-----------+
6 rows in set (0.00 sec)
--继续将上面SQL的查询结果看做一张临时表t2
mysql> SELECT t2.*,i2.f_color AS i2_color FROM (SELECT t1.*,i1.f_origin AS i1_origin FROM(SELECT f.f_name, f.f_price,f.f_origin,f.f_color, s.s_name FROM fruits f LEFT JOIN suppliers s ON f.s_id = s.s_id) t1 LEFT JOIN fruit_info i1 ON i1.id = t1.f_origin) t2 LEFT JOIN fruit_info i2 ON i2.id = t2.f_color WHERE t2.f_price >= 120;
+--------+---------+----------+---------+----------------+-----------+----------+
| f_name | f_price | f_origin | f_color | s_name | i1_origin | i2_color |
+--------+---------+----------+---------+----------------+-----------+----------+
| apple | 120 | 2 | 1 | 华东第一水果商 | 山东 | yellow |
| melon | 130 | 3 | 2 | 西北第一水果商 | 山西 | green |
| grape | 150 | 1 | 3 | 西北第一水果商 | 四川 | red |
+--------+---------+----------+---------+----------------+-----------+----------+
3 rows in set (0.00 sec)
注:
1、上面两种写法都是可以到达同一目的的:只是说第二种的写法稍微有点复杂(只是介绍下有这种写法,主要是想加强下把查询结果看做临时表的这种思想)
⑴将查询结果看做一张临时表,然后继续在这个临时表中过滤查询数据、这张临时表继续关联其他表等
⑵使用临时表时需要给临时表取别名且临时表中的字段名也需要别名
2、多张表之间的关联查询最好还是使用第一种的写法,毕竟这种写法要方便点
例11_2:
注:
1、使用第二种写法的好处就是:可以先过滤出数据在关联查询
⑴使用第一种写法"链式关联":是一次性将所有表都关联上了,最后在过滤数据(WHERE关键字必须位于ON关键字之后)。这样的话在关联时不符合条件的数据也会进行关联匹配
⑵使用第二种写法:可以先将数据过滤出来。这样就只有符合条件的数据才会进行关联匹配
⑶当然,第二种中的减少匹配次数指的是非第一次关联:第一次关联肯定是关联再过滤(WHERE关键字必须位于ON关键字之后)
例10:
-- 这里emp和dept使用的是左连接,所以"孙权"返回
-- 需要注意LEFT写的位置:emp和dept左连接,emp与salgrade依旧是内连接
mysql> SELECT e.name,d.name,s.id FROM emp e LEFT JOIN dept d ON e.dept_id = d.id JOIN salgrade s ON e.sal BETWEEN s.losat AND s.hisat;
+------+------------+----+
| name | name | id |
+------+------------+----+
| 刘备 | 后勤部 | 4 |
| 关羽 | 后勤部 | 2 |
| 张飞 | 后勤部 | 4 |
| 张角 | 财务部 | 4 |
| 张宝 | 财务部 | 1 |
| 张梁 | 财务部 | 5 |
| 曹操 | 人力资源部 | 1 |
| 曹丕 | 人力资源部 | 4 |
| 曹植 | 人力资源部 | 2 |
| 曹冲 | 人力资源部 | 1 |
| 孙权 | NULL | 3 |
+------+------------+----+
11 rows in set (0.00 sec)
例11:查询所有员工的部门、工资等级以及部门领导
-- 一定要注意LEFT的位置
mysql> SELECT e.name '员工',d.name,s.id,e1.name '领导' FROM emp e LEFT JOIN dept d ON e.dept_id = d.id JOIN salgrade s ON e.sal BETWEEN s.losat AND s.hisat LEFT JOIN emp e1 ON e.leader_id=e1.id;
+------+------------+----+------+
| 员工 | name | id | 领导 |
+------+------------+----+------+
| 刘备 | 后勤部 | 4 | 关羽 |
| 关羽 | 后勤部 | 2 | 刘备 |
| 张飞 | 后勤部 | 4 | 刘备 |
| 张角 | 财务部 | 4 | 张飞 |
| 张宝 | 财务部 | 1 | 关羽 |
| 张梁 | 财务部 | 5 | 刘备 |
| 曹操 | 人力资源部 | 1 | 曹操 |
| 曹丕 | 人力资源部 | 4 | 张角 |
| 曹植 | 人力资源部 | 2 | 孙权 |
| 曹冲 | 人力资源部 | 1 | 张宝 |
| 孙权 | NULL | 3 | 刘备 |
+------+------------+----+------+
11 rows in set (0.00 sec)