MySql多表查询
MySql多表查询
定西MySql
可能就是关于多表联查和一些数据库高级篇的部分吧,毕竟那块实在是薄弱.
1 多表查询
多表查询,也成为关联查询,指两个或更多个表一起完成的查询操作.
前提条件:这些一起查询的表之间是有关系的(一对一,一对多),它们之间一定是有关联字段的,这个关联字段可能是建立了外键,也可能没有建立外键,比如员工表和部门表,这两个表靠部门编号
进行关联
案例:3个表
- employees
- departments
- locations
三个表的关联关系:
1.1 多表查询如何实现
根据员工id查询员工对应的部门名
- 先看错误的实现方式(每个员工都与每个部门匹配了一遍):
1
2
3
4
5
6
7# 错误的实现方式:
select employee_id,department_name
FROM employees,departments #查询出2889条数据
SELECT * FROM employees # 107条
SELECT 2889/107 FROM DUAL # 结果为27
SELECT * FROM departments # 27个部门
为什么出现这种情况呢?现在就是107个员工都在27个部门中工作过,这种属于是求出了两个表的笛卡尔积,也叫出现笛卡尔积的错误
这个错误是怎么导致的呢,是因为缺少了多表的连接条件
笛卡尔积(虽然我以前学过)
笛卡尔积是一个数学运算,假设我有两个集合X和Y,那么X和Y的笛卡尔积就是X和Y的所有可能组合,也就是第一个对象来自于X,第二个对象来自于Y的所有可能,组合的个数即为两个集合中元素个数的乘积
笛卡尔积的错误会在下面条件下产生:
- 省略多个表的连接条件(或关联条件)
- 连接条件(或关联条件)无效
- 所有表中的所有行互相连接
为了避免笛卡尔积,可以在where中加上连接条件.
刚配置了一下md的代码片段,现在打个cc就可以出来反引号了,舒服了
加上连接条件:
1 | SELECT |
从sql优化的角度,建议多表查询时,每个字段前都指明其所在的表
因为有一个员工是没有部门id的,所以只有106条记录
给表起别名
由于表名特别长导致sql语句特别长时,可以给表起别名-在select和where中使用表的别名,注意,如果设置了表的别名,此时在select和where中不能使用表的原名了,必须使用别名
1 | SELECT |
?,我突然明白什么时候会用到外连接了,比如现在这种情况,我要查询所有的员工信息以及对应的部门信息,此时要设置连接条件,那就是两个部门id相等,此时如果没有部门id,那么该员工就不会出现在结果集中,怎么解决呢?就要使用到外连接了,看下面的sql
1 | SELECT employee_id,department_name |
此时是左连接,得到的结果是107条,其中包含一个没有部门id的员工
1 | SELECT employee_id,department_name |
此时是右连接,会返回除了每个员工以及对应的部门外,还会有没有员工的部门(但是此时并没有没有部门id的员工的信息)
现在理解了吧,如果是左连接,就会返回左边所有的记录,如果是右连接,就会返回右边所有的记录
没事,后面还会详细的讲
再加一个表
练习:查询员工的employee_id,last_name,department_name,city
1 | SELECT |
1.2 多表查询的分类
1.2.1 等值连接Vs非等值连接
像上面的例子,明显都是等值连接(连接条件,x=x,那就是等值连接),其余的条件是都是非等值连接
举个例子
1 | # 要查询员工的姓名,工资,和grade_level |
between..and..
和下面的意义是一样的,between后面紧跟下界,and后面紧跟上界
1.2.2 自连接Vs非自连接
不同的表之间进行连接-非自连接,同一张表进行的连接-自连接,也叫自我引用:
有没有很熟悉?之前的项目里面的菜单结构就是这个表,有id和parent_id
举例:
1 | # 练习: 查询员工id,员工姓名,及其管理者的id和姓名 |
此时也是106条,因为公司老大没有管理者,所以他没有在这个记录中,如果使用left join的话,应该就能出来了,试试
1 | SELECT |
此时就可以得到107条数据,里面包含了第一条没有管理者id的数据:
1.2.3 内连接Vs外连接
什么叫内连接:
上面写的都是内连接,随便拿一个:
1 | SELECT |
这就是内连接,emp.department_id = dept.department_id
会拿出左表和右表中满足条件的记录,左表,右表,就是from里面,左边的就是左表,右边的就是右表
官方解释:
合并具有同一列的两个以上的表的行,结果集中不包含一个表与另一个表不匹配的行
再看上面的sql,因为有一个员工是没有部门id的,所以跟右边的部门表无法匹配,就没有该条员工的记录,这就是内连接.还有一种情况,就是有些部门是没有对应的员工的,此时就会没有该条部门的记录
想要把左表中所有的数据,不管有没有匹配条件,就是左连接,反之,右连接
什么叫外连接
示例:
查询所有的员工的last_name和对应的部门信息(出了所有,一定就是一个外连接)
1 | SELECT |
官方解释:
合并具有同一列的两个以上的表的行,结果集中除了包含一个表与另一个表匹配的行之外,还查询到了左表/右表中不匹配的行
这里又涉及到SQL92和SQL99两种语法
SQL92的语法:
使用(+)
1 | SELECT |
当然,MySql不支持SQL92的语法中外连接的写法-有没有一种别骗的感觉哈哈哈,虽然mysql不支持,但是oracle是可以的
SQL99的语法:
使用JOIN ... ON
的形式进行多表查询,这种方式也能解决外连接的问题.MySql支持这种语法(SQL99的语法不仅可以实现外连接,也可以实现内连接)
- SQL99实现内连接:
1
2
3
4
5
6
7SELECT
emp.last_name,
dept.department_name,
l.city
FROM
employees emp
INNER JOIN departments dept ON emp.department_id = dept.department_id INNER JOIN locations l ON dept.location_id = l.location_id
注意,这里的INNER JOIN
和JOIN
是一样的,可以把JOIN
当作INNERJOIN
的简写
外连接就看下面的吧
所以这里外连接又分为三类,一类是左连接,一类是外连接,满外连接(这个之前没了解过)
这里员工表一共107条记录,有一条是没有部门id的,而部门表中一共是27条记录,有16个部门是没有员工的,此时如果是左外连接,那么就是107条,返回的结果中有一条记录没有部门信息,而如果是右4外连接,此时返回的结果就是106+16=122条,因为结果集中包含106条匹配的结果以及16个没有员工的部门信息
- 左外连接:
说白了就是比内连接多出了左表中不匹配的行,或者说是会查询出左表中所有的行
举个例子
查询所有的员工的last_name,department_name信息
1 | SELECT |
由于只有外连接才有LEFT/RIGHT
,所以这里的OUTER
也可以省略掉
- 右外连接:
同理,就是返回的结果中除了匹配的行,还会查出右表中不满足条件的行,或者说是会查出右表中所有的行
举个例子
查询所有部门的department_name以及对应的员工last_name信息
1 | SELECT |
此时就能查到122条数据,**一共106条有员工的部门,加上16条没有员工的部门
那如果想查询出左表和右表中所有的记录呢?就是不管匹配不匹配,都要查出来,就要用到满外连接
满外连接在sql99中的语法就是FULL JOIN
1 | SELECT |
但是,mysql不支持,mysql支持什么呢:
这就需要其他方式了:
看这个表,是7中join方式,我们想要实现full join,那么就需要借用另外两种join方式了-(图左中和右中)
在那之前,需要再了解一个内容:UNION的使用
UNION的使用
合并查询结果
利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集,合并时,两个表对应的列数和数据类型必须相同,并且相互对应,各个SELECT语句之间使用UNION或UNION ALL关键字分隔
语法格式:
1 | SELECT column,...FROM table1 |
UNION
和UNION ALL
的区别
UNION
操作符返回两个查询的结果集的并集,去除重复记录UNION ALL
操作符返回两个查询的结果集的并集,对于两个结果集的重复部分,不去重- 注意,执行UNION ALL语句时所需要的资源比UNION语句少,如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL语句,以提高数据查询的效率
就是这个意思,UNION就是123,而UNION ALL多了一个交集,为123+106
这时我们再想实现满外连接:(我们这里使用左上和右中图)
1 | SELECT |
此时就能实现查询到左表和右表中所有的数据了
7中JOIN的实现
把表再拿下来:
A就当成员工表,而B就是部门表
中图-内连接-查询结果应为106条
1 | SELECT |
左上图-左外连接-107条数据,包含一条没有部门的员工信息
1 | SELECT |
右上图-右外连接-122条数据,包含106个有部门信息的员工信息,以及16条没有员工的部门信息
1 | SELECT |
左中图-左外连接-1条数据,只会查到没有部门id的员工信息-且把两者相同的部分也给抹掉了
1 | SELECT |
右中图-右外连接-16条数据
1 | SELECT |
左下图-两个表的满外连接-得到全部的123条数据
- 左上图UNION ALL 右中图
1
2
3
4
5
6
7
8
9
10
11
12
13
14SELECT
emp.employee_id,
dept.department_id
FROM
employees emp
LEFT JOIN departments dept ON emp.department_id = dept.department_id UNION ALL
SELECT
emp.employee_id,
dept.department_id
FROM
employees emp
RIGHT JOIN departments dept ON emp.department_id = dept.department_id
WHERE
emp.department_id IS NULL - 右上图UNION ALL 左下图
1
2
3
4
5
6
7
8
9
10
11
12
13
14SELECT
emp.employee_id,
dept.department_id
FROM
employees emp
RIGHT JOIN departments dept ON emp.department_id = dept.department_id UNION ALL
SELECT
emp.employee_id,
dept.department_id
FROM
employees emp
LEFT JOIN departments dept ON emp.department_id = dept.department_id
WHERE
dept.department_id IS NULL
右下图-左中和右中UNION ALL–得到17条数据
1 | SELECT |
1.3 SQL99语法的新特性
1.3.1 自然连接
SQL99在SQL92的基础上提供了一些特殊语法,比如NATURAL JOIN
用来表示自然连接,我们可以把自然连接理解为SQL92中的等值连接,它会帮你自动查询两张连接表中所有哦相同的字段
,然后进行等值连接
1 | # 普通写法: |
就是会选择相同的字段进行等值连接
1.3.2 USING
原来的写法:
1 | SELECT |
用USING:
1 | SELECT |
效果是一样的.相当于替换掉employees.department_id = departments.department_id
1.4 练习题
- 显示
所有
员工的姓名,部门号,和部门名称
1 | SELECT |
- 查询部门号为90的员工的job_id和90号部门的location_id
1 | SELECT |
- 选择
所有有
奖金的员工的last_name,department_name,location_id,city
e.commission_pct是奖金
注意,这里是左连接,因为有一个人是有奖金,但是没有部门的:
1 | SELECT |
- 选择city在Toronto工作的员工的last_name,job_id,department_id,department_name
这里就不涉及到外连接了,因为只有有部门的员工才有city这个对应的属性
1 | SELECT |
- 查询员工所在的部门名称,部门地址,姓名,工作,工资,其中员工所在的部门的部门名称为’Executive’
1 | SELECT |
- 选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式:
employees Emp# manager Mgr#
1 | SELECT |
- 查询哪些部门没有员工
相当于员工表中没有的部门id,因为是is null啊,这样就排除了员工表中出现的部门id,也就是派出了有员工的部门,只剩下没有员工的部门
1 | SELECT |
- 查询哪个城市没有部门
1 | SELECT |
- 查询部门名为
Sales
或IT
的员工信息
1 | SELECT |
ok,课后题也加进来了,告一段落.