- 一、多表连接案例的举例
- 二、多表查询的分类
- 三、多表查询的课后练习
- 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_nameFROM 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.columnFROM table1, table2WHERE table1.column1 = table2.column2; #连接条件
正确的写法:
#案例:查询员工的姓名及其部门名称SELECT last_name, department_nameFROM employees, departmentsWHERE employees.department_id = departments.department_id;
如果在表中有相同列的时候,应该在列名之前添加上表名的前缀
SELECTusers.id id,users.`name` `name`,users.role_id,roles.`name`FROMt_users users,t_roles rolesWHEREusers.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_namefrom employees e,departments dwhere e.department_id = d.department_id;
一共会显示106条记录,因为有一位员工还没有分配部门,所以不会被查出来
join on实现左外连接
使用场景:部分员工可能暂时还未分配到部门
# 左外连接SELECT employee_id,department_nameFROM employees e LEFT JOIN departments dON e.department_id = d.department_id;

join on实现右外连接
使用场景:有些部门可能刚刚创建,暂时还没有分配员工
# 右外连接SELECT employee_id,department_nameFROM employees e RIGHT JOIN departments dON e.department_id = d.department_id;
join on实现三张表之间的外连接
# 三张表之间连接SELECT employee_id,department_name,cityFROM employees e LEFT JOIN departments dON e.department_id = d.department_idLEFT JOIN locations lON 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_nameFROM employees e JOIN departments dON e.department_id = d.department_id;# 左上图SELECT employee_id,department_nameFROM employees e LEFT JOIN departments dON e.department_id = d.department_id;# 右上图SELECT employee_id,department_nameFROM employees e RIGHT JOIN departments dON e.department_id = d.department_id;# 中左图SELECT employee_id,department_nameFROM employees e LEFT JOIN departments dON e.department_id = d.department_idWHERE d.department_id IS NULL;# 中右图SELECT employee_id,department_nameFROM employees e RIGHT JOIN departments dON e.department_id = d.department_idWHERE e.department_id IS NULL;# 下左图SELECT employee_id,department_nameFROM employees e LEFT JOIN departments dON e.department_id = d.department_idUNION ALLSELECT employee_id,department_nameFROM employees e RIGHT JOIN departments dON e.department_id = d.department_idWHERE e.department_id IS NULL;# 下右图SELECT employee_id,department_nameFROM employees e LEFT JOIN departments dON e.department_id = d.department_idWHERE d.department_id IS NULLUNION ALLSELECT employee_id,department_nameFROM employees e RIGHT JOIN departments dON e.department_id = d.department_idWHERE e.department_id IS NULL;
5. SQL99的语法新特性
5.1 新特性1: 自然连接
SQL99提供了一些特殊的语法,比如
NATURAL JOIN用来表示自然连接。我们把自然连接理解为SQL92中的等值连接。会自动查询两张连接表中所有相同的字段,然后进行等值连接。
# SQL92实现等值连接SELECT employee_id,last_name,department_nameFROM employees e JOIN departments dON e.department_id = d.department_idAND e.manager_id = d.manager_id# SQL99使用自然连接实现# 会找到两张表中所有相同的字段,然后进行等值连接SELECT employee_id,last_name,department_nameFROM employees e NATURAL JOIN departments d;
优点:语法简单
缺点:封装性强,有的时候并不需要查这么多字段,不能自定义查询某个字段
5.2 新特性2: USING连接
当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的 同名字段 进行等值连接。但是只能配 合JOIN一起使用。比如:
SELECT employee_id,last_name,department_nameFROM employees e JOIN departments dUSING (department_id);# 和下面这种写法是等价的SELECT employee_id,last_name,department_nameFROM employees e ,departments dWHERE e.department_id = d.department_id;
注意:超过三张表禁止使用
JOIN,因为JOIN相当于嵌套了一层循环,会大大提高查询所需要的时间
三、多表查询的课后练习
1. 显示所有员工的姓名,部门号和部门名称
SELECTe.first_name,e.last_name,e.department_id,d.department_nameFROMemployees e,departments dWHEREe.department_id = d.department_id;# 或SELECTe.first_name,e.last_name,e.department_id,d.department_nameFROMemployees eLEFT JOIN departments d ON e.department_id = d.department_id;
2. 查询90号部门员工的job_id和90号部门的location_id
SELECTjob_id,location_idFROMemployees eJOIN departments d ON e.department_id = d.department_idAND d.department_id = 90;
3. 选择所有有奖金的员工的
last_name , department_name , location_id , city
commission_pct奖金率
SELECTe.last_name,d.department_name,d.location_id,l.cityFROMemployees eLEFT JOIN departments d ON e.department_id = d.department_idLEFT JOIN locations l ON d.location_id = l.location_idWHEREe.commission_pct IS NOT NULL;
注意有的员工可能没有分配部门,但是其有奖学金,所以查询的时候需要使用左连接,避免查询的时候漏掉数据
4. 选择city在Toronto工作的员工的
last_name , job_id , department_id ,department_name
SELECTe.last_name,e.job_id,e.department_id,d.department_name,l.cityFROMemployees eJOIN departments d ON e.department_id = d.department_idJOIN locations l ON d.location_id = l.location_idWHEREl.city = 'Toronto';
5. 查询员工所在的部门名称、部门地址、姓名、工作、工资,
其中员工所在部门的部门名称为’Executive’
SELECTd.department_name,l.street_address,e.first_name,e.last_name,e.job_id,e.salary,j.job_titleFROMemployees eJOIN departments d ON e.department_id = d.department_idJOIN locations l ON d.location_id = l.location_idJOIN jobs j ON e.job_id = j.job_idWHEREd.department_name = 'Executive';
6. 选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号
SELECTe.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_idFROMemployees eLEFT JOIN employees m ON e.manager_id = m.employee_id;
7. 查询哪些部门没有员工
SELECTd.department_id,d.department_nameFROMemployees eRIGHT JOIN departments d ON e.department_id = d.department_idWHEREe.department_id IS NULL;
8. 查询哪些城市没有部门
SELECTl.location_id,l.cityFROMdepartments dRIGHT JOIN locations l ON d.location_id = l.location_idWHEREd.location_id IS NULL;
9. 查询部门名为 Sales 或 IT 的员工信息
SELECT*FROMdepartments dLEFT JOIN employees e ON d.department_id = e.department_idWHERE# d.department_name = 'Sales' OR d.department_name = 'IT';d.department_name IN ( 'Sales', 'IT' );
