- 一、多表连接案例的举例
- 二、多表查询的分类
- 三、多表查询的课后练习
- 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. 选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号
- 7. 查询哪些部门没有员工
- 8. 查询哪些城市没有部门
- 9. 查询部门名为 Sales 或 IT 的员工信息
多表查询,也可以叫做关联查询,指两个或更多张表一起完成查询操作。
前提条件:这些被一起查询的表之间是存在某种关联的(比如一对一,一对多,多对一),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键。比如:员工表和部门表之间,就是通过部门编号
进行关联
一、多表连接案例的举例
1.1 案例说明
从这多张表中获取数据:
#案例:查询员工的姓名及其部门名称
SELECT last_name, department_name
FROM employees, departments;
查询出来的结果如下,不难发现结果的数目是不正确的
+-----------+----------------------+
| last_name | department_name |
+-----------+----------------------+
| King | Administration |
| King | Marketing |
| King | Purchasing |
| King | Human Resources |
| King | Shipping |
| King | IT |
| King | Public Relations |
| King | Sales |
| King | Executive |
| King | Finance |
| King | Accounting |
| King | Treasury |
...
| Gietz | IT Support |
| Gietz | NOC |
| Gietz | IT Helpdesk |
| Gietz | Government Sales |
| Gietz | Retail Sales |
| Gietz | Recruiting |
| Gietz | Payroll |
+-----------+----------------------+
2889 rows in set (0.01 sec)
1.2 笛卡尔积的错误
相当于employees
中的last_name
字段和dapartments
表中的每一个department_name
都做了笛卡尔积运算。导致最后出现了2889
条数据
1.3 问题的解决
- 笛卡尔积的错误会在下面的条件中产生:
- 省略多个表的连接条件(或关联条件)
- 连接条件(或关联条件)无效 => 相当于没指定条件
- 所有表中的行互相连接
为了避免笛卡尔积,可以在
WHERE
中加入有效的连接条件。SELECT table1.column, table2.column
FROM table1, table2
WHERE table1.column1 = table2.column2; #连接条件
正确的写法:
#案例:查询员工的姓名及其部门名称
SELECT last_name, department_name
FROM employees, departments
WHERE employees.department_id = departments.department_id;
如果在表中有相同列的时候,应该在列名之前添加上表名的前缀
SELECT
users.id id,
users.`name` `name`,
users.role_id,
roles.`name`
FROM
t_users users,
t_roles roles
WHERE
users.role_id = roles.id;
二、多表查询的分类
分为以下三个角度
- 等值连接和非等值连接
- 自连接和非自连接
- 内连接和外连接
1. 等值连接和非等值连接
等值连接
:查询的条件是相等
的关系非等值连接
:查询的条件是非相等的关系
,比如大于,小于等等
2. 自连接和非自连接
自连接
:表中有一个字段和自己本身相关联
比如管理者和普通员工都属于员工表,但是普通员工会有一个
manage_id
字段指向其管理者,这就是典型的自连接
3. 内连接和外连接
内连接
:结果集中不包含不满足一张表和另一张表不匹配的部分外连接
:结果集中除了满足两张表匹配的部分,还有不满足条件的部分
- 左外连接:左表中的数据全部保留,右表中能匹配的就正确显示内容,不匹配的则为NULL
- 右外连接:右表中的数据全部保留,左表中能匹配的就是正确显示数据,不能匹配的就为NULL
- 满外连接:两张表中的数据都全部保留
注意:MySQL不支持满外连接**(FULL OUTER JOIN)**
查询
SQL99和SQL92
:mysql
不支持sql92的+
来实现外连接,在mysql中,我们使用join on
的方式实现外连接,但是在其他的DBMS
中,比如Oracle
就支持sql92
语法
普通内连接
# 内连接:结果集中不包含一个表和另一个表中不匹配的行
# 只取同时满足两边条件的数据
select employee_id,department_name
from employees e,departments d
where e.department_id = d.department_id;
一共会显示106条记录,因为有一位员工还没有分配部门,所以不会被查出来
join on
实现左外连接
使用场景:部分员工可能暂时还未分配到部门
# 左外连接
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id;
join on
实现右外连接
使用场景:有些部门可能刚刚创建,暂时还没有分配员工
# 右外连接
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id;
join on
实现三张表之间的外连接
# 三张表之间连接
SELECT employee_id,department_name,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;
**SQL99**
语法下的7种外连接方式
4. 满外连接
4.1 UNION 合并查询
利用UNION关键字,可以给出多条select
语句,并且将多个结果集合并成为一个结果集。但是,合并的时候,两张表对应的列数和数据类型必须相同,并且相互对应。各select
语句之间使用union
或union all
来连接UNION
:返回两个结果集的并集,并去除中间重复的部分UNION ALL
:返回两个结果集的并集,但是不去除重复的部分
UNION ALL
查询的时候占用的资源更少。一般来说,如果不需要对数据进行去重,或者查询的时候明确知道不会有重复的数据,那么就优先使用UNION ALL
进行查询。
4.2 七种JOIN的实现
# 中图
SELECT employee_id,department_name
FROM employees e JOIN departments d
ON e.department_id = d.department_id;
# 左上图
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id;
# 右上图
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id;
# 中左图
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL;
# 中右图
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.department_id IS NULL;
# 下左图
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
UNION ALL
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.department_id IS NULL;
# 下右图
SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL
UNION ALL
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.department_id IS NULL;
5. SQL99的语法新特性
5.1 新特性1: 自然连接
SQL99提供了一些特殊的语法,比如
NATURAL JOIN
用来表示自然连接。我们把自然连接理解为SQL92中的等值连接。会自动查询两张连接表中所有相同的字段
,然后进行等值连接
。
# SQL92实现等值连接
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.department_id = d.department_id
AND e.manager_id = d.manager_id
# SQL99使用自然连接实现
# 会找到两张表中所有相同的字段,然后进行等值连接
SELECT employee_id,last_name,department_name
FROM employees e NATURAL JOIN departments d;
优点:语法简单
缺点:封装性强,有的时候并不需要查这么多字段,不能自定义查询某个字段
5.2 新特性2: USING连接
当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的 同名字段 进行等值连接。但是只能配 合JOIN一起使用。比如:
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
USING (department_id);
# 和下面这种写法是等价的
SELECT employee_id,last_name,department_name
FROM employees e ,departments d
WHERE e.department_id = d.department_id;
注意:超过三张表禁止使用
JOIN
,因为JOIN
相当于嵌套了一层循环,会大大提高查询所需要的时间
三、多表查询的课后练习
1. 显示所有员工的姓名,部门号和部门名称
SELECT
e.first_name,
e.last_name,
e.department_id,
d.department_name
FROM
employees e,
departments d
WHERE
e.department_id = d.department_id;
# 或
SELECT
e.first_name,
e.last_name,
e.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
SELECT
job_id,
location_id
FROM
employees e
JOIN departments d ON e.department_id = d.department_id
AND d.department_id = 90;
3. 选择所有有奖金的员工的
last_name , department_name , location_id , city
commission_pct
奖金率
SELECT
e.last_name,
d.department_name,
d.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 ,department_name
SELECT
e.last_name,
e.job_id,
e.department_id,
d.department_name,
l.city
FROM
employees e
JOIN departments d ON e.department_id = d.department_id
JOIN locations l ON d.location_id = l.location_id
WHERE
l.city = 'Toronto';
5. 查询员工所在的部门名称、部门地址、姓名、工作、工资,
其中员工所在部门的部门名称为’Executive’
SELECT
d.department_name,
l.street_address,
e.first_name,
e.last_name,
e.job_id,
e.salary,
j.job_title
FROM
employees e
JOIN departments d ON e.department_id = d.department_id
JOIN locations l ON d.location_id = l.location_id
JOIN jobs j ON e.job_id = j.job_id
WHERE
d.department_name = 'Executive';
6. 选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号
SELECT
e.first_name emp_first_name,
e.last_name emp_last_name,
e.employee_id emp_id,
e.manager_id emp_manag_id,
m.first_name manag_first_name,
m.last_name manag_last_name,
m.employee_id mang_emp_id
FROM
employees e
LEFT JOIN employees m ON e.manager_id = m.employee_id;
7. 查询哪些部门没有员工
SELECT
d.department_id,
d.department_name
FROM
employees e
RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE
e.department_id IS NULL;
8. 查询哪些城市没有部门
SELECT
l.location_id,
l.city
FROM
departments d
RIGHT JOIN locations l ON d.location_id = l.location_id
WHERE
d.location_id IS NULL;
9. 查询部门名为 Sales 或 IT 的员工信息
SELECT
*
FROM
departments d
LEFT JOIN employees e ON d.department_id = e.department_id
WHERE
# d.department_name = 'Sales' OR d.department_name = 'IT';
d.department_name IN ( 'Sales', 'IT' );