当前位置:网站首页>[MySQL] 多表查询
[MySQL] 多表查询
2022-08-01 04:52:00 【计科_xiao_cai_ji】
图
资料
根据【MySQL数据库教程天花板,mysql安装到mysql高级,强!硬!】 整理
资料链接:
百度网盘:
链接:https://pan.baidu.com/s/1KboU_3EZJxrezMWZ2klP6g
提取码:1234
阿里云盘
【MySQL】
1 为什么需要多表查询
多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。
前提条件:这些一起查询的表之间是有关系的(一对一、一对多、多对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键。比如:员工表和部门表,这两个表依靠“部门编号”进行关联。
引入案例:查询员工名为’Abel’的人在哪个城市工作
# 先查询 Abel 所在的部门的部门编号
SELECT department_id
FROM employees
WHERE last_name='Abel'; # 部门编号 80
# 根据 Abel 所在部门的部门编号查询部门所在城市的城市编号
SELECT location_id
FROM departments
WHERE department_id=80; # 城市编号 2500
# 根据部门所在城市的城市编号查询城市名
SELECT city
FROM locations
WHERE location_id=2500; # Oxford
完成该查询,需要三条SQL语句进行三次查询,于是有了多表查询 — 将多张表进行联合查询,将多张表合并为一张表进行查询。
2 笛卡尔积(或交叉连接)的理解
SELECT last_name, department_name, city
FROM employees, departments, locations
WHERE last_name='Abel';
直接对三张表进行合并然后查询,会发现查询出来的数据不对,结果的条数远大于预期。(这是由于出现了笛卡尔积的错误,错误的原因:缺少了每个表之间的连接条件)
员工表中姓名为 Abel 的记录与每个部门和每个城市都匹配了一遍。
笛卡尔积是一个数学运算。
假设我有两个集合 X 和 Y,两个集合中的每个元素为一条记录,那么 X 和 Y 的笛卡尔积就是 X 和 Y 中的每条记录(每个元素)所有可能的组合组成的结果,即两个集合中的每条记录(每个元素)进行两两组合。组合的个数即为两个集合中记录条数(元素个数)的乘积数。
SQL92中,笛卡尔积也称为 交叉连接 ,英文是 CROSS JOIN 。在 SQL99 中也是使用 CROSS JOIN表示交叉连接。
它的作用就是可以把任意表进行笛卡尔积连接,即使这两张表不相关。
在MySQL中如下情况会出现笛卡尔积:
SELECT * FROM employees, departments;
# 或
SELECT * FROM employees CROSS JOIN departments;
2.1 笛卡尔积分析与问题解决
- 笛卡尔积的错误会在下面条件下产生:
- 1.省略多个表的连接条件(或关联条件)
- 2.连接条件(或关联条件)无效
- 3.所有表中的所有行互相连接
为了避免笛卡尔积, 可以在 WHERE 加入有效的连接条件。
三张表的关联关系:
可以据此增加筛选条件,WHERE子句中写入连接条件,去除不需要的数据,避免笛卡尔积的产生。
在不同的表中有相同的列名时,在列名之前加上表名前缀避免报错:
SELECT last_name, department_name, city
FROM employees, departments, locations
WHERE last_name='Abel'
AND employees.department_id=departments.department_id
AND locations.location_id = departments.location_id;
- 建议:从sql优化的角度,建议多表查询时,每个字段前都指明其所在的表。
- 因为如果不增加字段所在的表,在SQL查询时数据库还要查询该字段在那个表中,会影响查询的效率,如果指定了字段所在的表,则数据库可以直接找字段所在的表,增加查询效率。
3 表的别名
- 可以给表起别名,在 SELECT 和 WHERE 中使用表的别名。
- 在执行查询语句时,执行顺序为
FROM => SELECT => WHERE => ...
,所以表的别名可以在 SELECT 和 WHERE 中使用。 - 使用别名可以简化查询。
- 列名前使用表名前缀可以提高查询效率。
- 在执行查询语句时,执行顺序为
SELECT e.last_name, d.department_name, l.city
FROM employees e, departments d, locations l
WHERE e.last_name='Abel'
AND e.department_id=d.department_id
AND l.location_id = d.location_id;
如果给表起了别名,一旦在 SELECT 或 WHERE 中使用表名的话,则必须使用表的别名,而不能再使用表的原名。
4 连接多个表
如果有n个表实现多表的查询,则需要至少n-1个连接条件
连接 n个表,至少需要n-1个连接条件。比如,连接三个表,至少需要两个连接条件。
如图:
员工表和部门表通过部门编号进行关联,部门表与城市表通过城市编号进行关联。
查询员工的employee_id,last_name,department_name,city:
# 查询员工的employee_id,last_name,department_name,city
SELECT e.employee_id, e.last_name, d.department_name, l.city
FROM employees e, departments d, locations l
WHERE e.employee_id=d.department_id
AND d.location_id=l.location_id;
5 多表查询的分类
- 等值连接 vs 非等值连接
- 自连接 vs 非自连接
- 内连接 vs 外连接
5.1 等值连接 vs 非等值连接
5.1.1 等值连接
表的连接条件中,选取的是两张表中指定字段相等的记录
# 查询员工的employee_id,last_name,department_name,city
SELECT e.employee_id, e.last_name, d.department_name, l.city
FROM employees e, departments d, locations l
WHERE e.employee_id=d.department_id
AND d.location_id=l.location_id;
5.1.2 非等值连接
表的连接条件中,选取的不是两张表中指定字段相等的记录
工资等级表:
查询员工的工资等级:
SELECT employees.last_name, employees.salary, job_grades.grade_level
FROM employees, job_grades
WHERE employees.salary BETWEEN job_grades.lowest_sal AND job_grades.highest_sal;
5.2 自连接 vs 非自连接
5.2.1 自连接
一张表,自己与自己进行连接。用取别名的方式虚拟成两张表以代表不同的意义。然后两个表再进行内连接,外连接等查询。
自连接的例子:
查询员工id,员工姓名及其管理者的id和姓名
# 查询员工id,员工姓名及其管理者的id和姓名
SELECT e.employee_id, e.last_name, m.employee_id
FROM employees e, employees m
WHERE e.manager_id = m.employee_id
5.2.2 非自连接
一张表与另一张表进行连接查询。
非自连接的例子:
查询员工的工资等级:
SELECT employees.last_name, employees.salary, job_grades.grade_level
FROM employees, job_grades
WHERE employees.salary BETWEEN job_grades.lowest_sal AND job_grades.highest_sal;
5.3 内连接
内连接:合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行,即内连接的查询结果集中都是两张表中能够相互匹配的记录根据连接条件组成的结果,不存在一个表中有的记录而另一张表中没有与之相匹配的记录组成的结果。
查询员工编号及其所在的部门:
SELECT employee_id,department_name
FROM employees e,departments d
WHERE e.`department_id` = d.department_id;
不包含没有部门的员工
SQL92语法实现内连接:见上,略
SQL99语法中使用 JOIN …ON 的方式实现多表的查询。
SQL99语法实现内连接:
两张表:
SELECT last_name, department_name
FROM employees
INNER JOIN departments
ON employees.department_id = departments.department_id;
三张表:
SELECT last_name, department_name, city
FROM employees
INNER JOIN departments
ON employees.department_id = departments.department_id
INNER JOIN locations
ON departments.location_id = locations.location_id;
5.4 外连接
外连接:合并具有同一列的两个以上的表的行, 结果集中除了包含一个表与另一个表匹配的行之外,还查询到了左表 或 右表中不匹配的行。
外连接的分类:
- 左外连接
两个表在连接过程中除了返回满足连接条件的行以外还返回左表中不满足条件的行,这种连接称为左外连接。 - 右外连接
两个表在连接过程中除了返回满足连接条件的行以外还返回右表中不满足条件的行,这种连接称为右外连接。 - 满外连接
左外连接与右外连接的并集
5.4.1 使用(+)实现外连接
SQL92语法实现外连接:使用 + ---------- MySQL不支持SQL92语法中外连接的写法!
查询所有的员工的last_name,department_name信息 :
需要使用左外连接
使用SQL92:(MySQL不支持)
SELECT employee_id,department_name
FROM employees e,departments d
# 需要完全保留的表 对应的另一张表 使用(+)
# 相当于使用(+)对于数据的少的表使用 Null 进行补全
WHERE e.`department_id` = d.department_id(+);
5.4.1 SQL99外连接
SQL99语法中使用 JOIN …ON 的方式实现多表的查询。这种方式也能解决外连接的问题。MySQL是支持此种方式的。
LEFT JOIN 和 RIGHT JOIN 只存在于 SQL99 及以后的标准中,在 SQL92 中不存在,只能用 (+) 表示。
使用SQL99实现外连接:
查询所有的员工的last_name, department_name信息
5.4.2 左外连接
SELECT last_name, department_name
FROM employees e
LEFT OUTER JOIN departments d
ON e.department_id=d.department_id;
# outer 可以省略
SELECT last_name, department_name
FROM employees e
LEFT JOIN departments d
ON e.department_id=d.department_id;
5.4.3 右外连接
SELECT last_name, department_name
FROM employees e
RIGHT JOIN departments d
ON d.department_id = e.department_id;
5.4.4 满外连接
- 满外连接的结果 = 左右表匹配的数据 + 左表没有匹配到的数据 + 右表没有匹配到的数据。
- SQL99是支持满外连接的。使用FULL JOIN 或 FULL OUTER JOIN来实现。
- 需要注意的是,MySQL不支持FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT JOIN 代替。
mysql不支持FULL OUTER JOIN:
SELECT last_name, department_name
FROM employees e
FULL OUTER JOIN departments d
ON d.department_id = e.department_id;
6 UNION 和 UNION ALL
合并查询结果 利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并时,两个表对应的列数和数据类型必须相同,并且相互对应。
各个SELECT语句之间使用UNION或UNION ALL关键字分隔。
语法格式:
SELECT column,... FROM table1
UNION [ALL]
SELECT column,... FROM table2
5.1 UNION
UNION:会执行去重操作
UNION 操作符返回两个查询的结果集的并集,去除重复记录。
5.2 UNION ALL
UNION ALL:不会执行去重操作
UNION ALL 操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。
注意:
执行UNION ALL语句时所需要的资源比UNION语句少。如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL语句,以提高数据查询的效率。(不用去重提高效率)
7 7种SQL JOINS的实现
基于 employees、departments 两张表实现上图七种连接操作。
7.1 左上图:左外连接
# 左上图:左外连接
SELECT e.employee_id, d.department_id
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id;
7.2 右上图:右外连接
# 右上图:右外连接
SELECT e.employee_id, d.department_id
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id;
7.3 左中图
左中图要查询的部分为左外连接去除内连接的部分,即筛选出两表根据连接条件进行连接后右表为空的部分。
# 左中图
SELECT e.employee_id, d.department_id
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL ;
7.4 中图:内连接
# 中图:内连接
SELECT e.employee_id, d.department_id
FROM employees e
JOIN departments d
ON e.department_id = d.department_id;
7.5 右中图
右中图要查询的部分为右外连接去除内连接的部分,即筛选出两表根据连接条件进行连接后左表为空的部分。
# 右中图
SELECT e.employee_id, d.department_id
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.employee_id IS NULL ;
7.6 左下图:满外连接
左下图可以由左上图与右上图进行并集操作实现,或由左上图与右中图进行并集操作实现,或由左中图与右上图进行并集操作实现。
通过由左上图与右上图进行并集操作实现,还有执行去重操作,只能使用 UNION ,而通过左上图与右中图进行并集操作实现,或左中图与右上图进行并集操作实现,不需要进行去重操作,可以使用 UNION ALL。
对比之下,通过左上图与右中图进行并集操作实现,或左中图与右上图进行并集操作实现,效率更高。
# 左上图:左外连接
SELECT e.employee_id, d.department_id
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
UNION ALL
# 右中图
SELECT e.employee_id, d.department_id
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.employee_id IS NULL ;
# 右上图:右外连接
SELECT e.employee_id, d.department_id
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id
UNION ALL
# 左中图
SELECT e.employee_id, d.department_id
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL ;
7.7 右下图
可以通过左中图与右中图进行并集操作实现。
# 右下图
# 左中图
SELECT e.employee_id, d.department_id
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL
UNION ALL
# 右中图
SELECT e.employee_id, d.department_id
FROM employees e
RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.employee_id IS NULL ;
8 自然连接
自然连接会自动查询两张连接表中所有字段名相同的字段,然后根据所有字段名相同的字段进行等值连接。可以把自然连接理解为多个字段的等值连接。
SQL99 在 SQL92 的基础上提供了 NATURAL JOIN 用来表示自然连接。
在 employees 表中和 departments 表中具有两个字段名相同的字段:
基于 employees 表和 departments 表进行自然连接查询:
# 自然连接查询
SELECT
e.employee_id,
e.department_id,
d.department_id,
e.manager_id,
d.manager_id
FROM employees e
NATURAL JOIN departments d;
等价于:
SELECT
e.employee_id,
e.department_id,
d.department_id,
e.manager_id,
d.manager_id
FROM employees e
JOIN departments d
ON e.department_id = d.department_id AND
e.manager_id = d.manager_id;
9 USING 连接
进行连接的时候,SQL99 还支持使用 USING 指定数据表里的 同名字段 进行等值连接。但是只能配合JOIN一起使用。
使用 USING 指定数据表里的同名字段后,会自动在两个表中查找指定的字段,根据指定的字段进行等值连接。
SELECT e.employee_id, d.department_id
FROM employees e
JOIN departments d
USING (department_id);
等价于:
SELECT e.employee_id, d.department_id
FROM employees e
JOIN departments d
ON e.department_id = d.department_id;
10 补充
在使用 JOIN 进行连接查询时,可以将连接条件一起写在连接的后面,如下:
SELECT last_name,job_title,department_name
FROM employees
INNER JOIN departments
INNER JOIN jobs
ON employees.department_id = departments.department_id AND
employees.job_id = jobs.job_id;
建议:
一个连接后面跟着对应的连接条件,即一个 JOIN 后面跟着对应的 ON。
SELECT last_name,job_title,department_name
FROM employees
INNER JOIN departments
ON employees.department_id = departments.department_id
INNER JOIN jobs
ON employees.job_id = jobs.job_id;
11 多表查询练习
【题目】
# 1.显示所有员工的姓名,部门号和部门名称。
# 2.查询90号部门员工的job_id和90号部门的location_id
# 3.选择所有有奖金的员工的 last_name , department_name , location_id , city
# 4.选择city在Toronto工作的员工的 last_name , job_id , department_id , department_name
# 5.查询员工所在的部门名称、部门地址、姓名、工作、工资,其中员工所在部门的部门名称为’Executive’
# 6.选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
# employees Emp# manager Mgr#
# kochhar 101 king 100
# 7.查询哪些部门没有员工
# 8. 查询哪个城市没有部门
# 9. 查询部门名为 Sales 或 IT 的员工信息
【解答】
1.显示所有员工的姓名,部门号和部门名称。
# 1.显示所有员工的姓名,部门号和部门名称。
SELECT e.last_name, d.department_id, d.department_name
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id;
2.查询90号部门员工的job_id和90号部门的location_id
# 2.查询90号部门员工的job_id和90号部门的location_id
SELECT e.job_id, d.location_id
FROM employees e
JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id = 90;
或
SELECT e.job_id, d.location_id
FROM employees e, departments d
WHERE e.department_id = d.department_id AND
d.department_id = 90;
3.选择所有有奖金的员工的 last_name , department_name , location_id
# 3.选择所有有奖金的员工的 last_name , department_name , location_id , city
SELECT e.last_name, d.department_name, l.location_id, l.city
FROM employees e
LEFT JOIN departments d
ON e.department_id = d.department_id
LEFT JOIN locations l
ON d.location_id = l.location_id
WHERE e.commission_pct IS NOT NULL ;
4.选择city在Toronto工作的员工的 last_name , job_id , department_id
# 4.选择city在Toronto工作的员工的 last_name , job_id , department_id , department_name
SELECT e.last_name, e.job_id, d.department_id, d.department_name
FROM employees e
JOIN departments d
ON e.department_id = d.department_id
JOIN locations l
ON d.location_id = l.location_id
WHERE city = 'Toronto';
或
SELECT e.last_name,
e.job_id,
d.department_id,
d.department_name
FROM employees e,
departments d,
locations l
WHERE e.department_id = d.department_id AND
d.location_id = l.location_id AND
l.city = 'Toronto';
5.查询员工所在的部门名称、部门地址、姓名、工作、工资,其中员工所在部门的部门名称为’Executive’
# 5.查询员工所在的部门名称、部门地址、姓名、工作、工资,其中员工所在部门的部门名称为’Executive’
SELECT d.department_name,
l.city,
e.last_name,
e.job_id,
e.salary
FROM employees e
JOIN departments d
ON e.department_id = d.department_id
JOIN locations l
ON d.location_id = l.location_id
WHERE department_name = 'Executive';
或
SELECT d.department_name,
l.city,
e.last_name,
e.job_id,
e.salary
FROM employees e, departments d, locations l
WHERE e.department_id = d.department_id AND
d.location_id = l.location_id AND
department_name = 'Executive';
6.选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
employees | Emp# | manager | Mgr# |
---|---|---|---|
kochhar | 101 | king | 100 |
# 6.选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
# employees Emp# manager Mgr#
# kochhar 101 king 100
SELECT e.last_name "employees",
e.employee_id "Emp#",
m.last_name "manager",
m.employee_id "Mgr#"
FROM employees e
LEFT JOIN employees m
ON e.manager_id = m.employee_id;
7.查询哪些部门没有员工
# 7.查询哪些部门没有员工
SELECT d.department_id
FROM departments d
LEFT JOIN employees e
ON d.department_id = e.department_id
WHERE e.employee_id IS NULL;
- 查询哪个城市没有部门
# 8. 查询哪个城市没有部门
SELECT l.city, l.location_id
FROM locations l
LEFT JOIN departments d
ON l.location_id = d.location_id
WHERE d.department_id IS NULL ;
- 查询部门名为 Sales 或 IT 的员工信息
# 9. 查询部门名为 Sales 或 IT 的员工信息
SELECT e.employee_id,
e.last_name,
d.department_id,
d.department_name
FROM employees e
JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_name IN ('Sales', 'IT');
或
SELECT e.employee_id,
e.last_name,
d.department_id,
d.department_name
FROM employees e, departments d
WHERE e.department_id = d.department_id AND
d.department_name IN ('Sales', 'IT');
边栏推荐
- MySQL Practice Summary -
- Excuse me, only primary key columns can be queried using sql in table storage. Does ots sql not support non-primary keys?
- SQL Analysis of ShardingSphere
- 【愚公系列】2022年07月 Go教学课程 025-递归函数
- Excel record of integer programming optimization model to solve the problem
- 干货!如何使用仪表构造SRv6-TE性能测试环境
- 故乡的素描画
- (2022 Niu Ke Duo School IV) N-Particle Arts (Thinking)
- 【愚公系列】2022年07月 Go教学课程 024-函数
- 数组问题之《下一个排列》、《旋转图像》以及二分查找之《搜索二维矩阵》
猜你喜欢
基于Arduino制作非接触式测温仪
6-23漏洞利用-postgresql代码执行利用
typescript27 - what about enumeration types
PAT乙级 1002 写出这个数
初识shell脚本
数组问题之《两数之和》以及《三数之和 》
【愚公系列】2022年07月 Go教学课程 025-递归函数
Simulation of Active anti-islanding-AFD Active Anti-islanding Model Based on Simulink
typescript28 - value of enumeration type and data enumeration
typescript19-对象可选参数
随机推荐
7月编程排行榜来啦!这次有何新变化?
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
【愚公系列】2022年07月 Go教学课程 024-函数
【无标题】
Progressive Reconstruction of Visual Structure for Image Inpainting 论文笔记
Pyspark Machine Learning: Vectors and Common Operations
scheduleWithFixedDelay和scheduleAtFixedRate的区别
2022-07-31: Given a graph with n points and m directed edges, you can use magic to turn directed edges into undirected edges, such as directed edges from A to B, with a weight of 7.After casting the m
动态规划 01背包
PAT乙级 1002 写出这个数
深圳某游戏研发公司给每个工位都装监控,网友:堪比坐牢!
(2022牛客多校四)D-Jobs (Easy Version)(三维前缀或)
【愚公系列】2022年07月 Go教学课程 025-递归函数
MySQL-DML语言-数据库操作语言-insert-update-delete-truncate
最新 955 不加班的公司名单
李迟2022年7月工作生活总结
typescript20-接口
mysql中解决存储过程表名通过变量传递的方法
(2022牛客多校四)N-Particle Arts(思维)
力扣(LeetCode)212. 单词搜索 II(2022.07.31)