目录
一、一个案例引发的多表连接
先看这三张表的结构:
员工表的department_id关联部门表的主键department_id,而部门表的location_id关联地区表的主键location_id。
意思就是员工所属部门,而部门所属地区,从而可以查出员工所属地区在哪。
二、笛卡尔积的错误和与正确的多表查询
2.1、笛卡尔积错误展示
案例:查询员工的姓名及其部门名称
SELECT last_name, department_name FROM employees, departments;
结果怎么查出这么多条记录?
我员工表只有107条记录,而部门表更少,只有27条记录,那2889条记录是怎么来的呢?
学过数学的小伙伴们估计已经猜到了答案,是107 * 27 = 2889。是每个员工都与每个部门匹配的一遍,这就是笛卡尔积。
2.2、笛卡尔积解决方法
SQL92中,笛卡尔积也称为交叉连接 ,英文是 CROSS JOIN 。在 SQL99 中也是使用 CROSS JOIN表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。
说白了,其实就是没有写连接条件,修改如下:
SELECT last_name, department_name FROM employees, departments where employees.department_id = departments.department_id;
2.3、练习
问题:查出员工所在的部门和地区,要求显示出员工名、部门名、地区名
SELECT
last_name,
department_name,
city
FROM
employees,
departments,
locations
WHERE
employees.department_id = departments.department_id
AND departments.location_id = locations.location_id;
从SQL语句看出,需要三张表关联查询,条件先是员工表的department_id关联部门表的department_id,然后部门表的location_id再关联地区表的location_id。
三、多表查询分类
3.1、等值连接 vs 非等值连接
顾名思义,之前我们where条件都是谁等于谁,非等值连接就是不等于谁。
例如:我们先看员工表和工作等级表结构
现在想查出每个员工的薪资等级。
SELECT
e.last_name,
e.salary,
j.grade_level
FROM
employees e,
job_grades j
WHERE
e.salary >= j.lowest_sal
AND e.salary <= j.highest_sal
这就叫非等值连接。
3.2、自连接 vs 非自连接
先看看员工表的manager_id(管理者ID)。
问题:查询员工id,员工姓名及其管理者的id和姓名
注意:管理者id要和employee_id相等。
SELECT
emp.employee_id,
emp.last_name,
mgr.employee_id,
mgr.last_name
FROM
employees emp,
employees mgr
WHERE
emp.manager_id = mgr.employee_id
顾名思义,自己和自己连接,叫做自连接。
3.3、内连接 vs 外连接
内连接(inner join)
是将两个或多个表中满足指定条件的行连接在一起,形成一个新的结果集的操作。
如:
SELECT
e.last_name,
dept.department_name
FROM
employees e
INNER JOIN departments dept ON e.department_id = dept.department_id
外连接
左外连接(left join)
左表数据全部查出,右表只返回符合条件的数据。
例如:查询全部的员工和所在部门
员工表共有107条数据,其中有位员工是没有部门的,如果是内连接,最后返回的是106条数据,不包括没有部门的那位员工,但我现在的需求是包含这位员工,这就用到我们的左外连接。
SELECT
e.last_name,
dept.department_name
FROM
employees e
LEFT JOIN departments dept ON e.department_id = dept.department_id;
右外连接(right join)
右表数据全部查出,左表只返回符合条件的数据。
例如:查询全部的部门和部门下的员工
SELECT
e.last_name,
dept.department_name
FROM
employees e
RIGHT JOIN departments dept ON e.department_id = dept.department_id;
右表中有的部门下并没有员工,有的部门下有很多员工。
其实可以这样想,因为我是右外连接,我以右表为主,拿每一个部门都去和左表所有记录进行比对,符合条件的就记录下来,不符合的把右表记录下来,其实就相当于Java里的双层for循环。
for (int i = 0, i < dept.size(), i++) { // 遍历部门表
for (int j = 0, i < emp.size(), j++) { //遍历员工表
// 拿部门表的部门id和员工表的部门id比对
if (dept.getDepartmentId().equals(emp.getDepartmentId())) {
}
}
}
3.4、七种JOINS查询
左1:左外连接(left join)
右1:右外连接(right join)
中间:内连接(inner join)
左2图
例:
SELECT
e.last_name,
dept.department_name
FROM
employees e
LEFT JOIN departments dept ON e.department_id = dept.department_id
WHERE
dept.department_id IS NULL;
如图所示,如果sql不加where条件,单纯的左外连接,那查出的是左表全部数据,当然也包括没有部门的那位员工,符合左1图,但现在加了where dept.department_id IS NULL条件,我就把没有部门的那位员工给单独取出来了,注意左2图红色部分是不满足on后面的条件的那部分。
右2图
SELECT
e.last_name,
dept.department_name
FROM
employees e
RIGHT JOIN departments dept ON e.department_id = dept.department_id
WHERE
e.department_id IS NULL;
左下图
属于满外连接,你可以像拼图一样,将左1图和右2图用union拼在一起。
SELECT
last_name,
department_name
FROM
employees e
LEFT JOIN departments dept ON e.department_id = dept.department_id
UNION ALL
SELECT
last_name,
department_name
FROM
employees e
RIGHT JOIN departments dept ON e.department_id = dept.department_id
WHERE
e.department_id IS NULL;
右下图
也可以用拼图的方式,将左2和右2使用union all连接起来。
3.5、自然连接(natural join)
它会帮你自动查询两张连接表中所有相同的字段,然后进行等值连接。
比如我们看员工表和部门表
有两个相同的条件。那么如下是自然连接的写法,等于普通连接的写法。
# 自然连接
SELECT
employee_id,
last_name,
department_name
FROM
employees e
NATURAL JOIN departments d;
# 普通连接
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;
3.6、using用法
using用来简写on的条件。
如下:
# 内连接 on后写条件
SELECT
e.last_name,
dept.department_name
FROM
employees e
INNER JOIN departments dept ON e.department_id = dept.department_id;
# 内连接 using写条件
SELECT
e.last_name,
dept.department_name
FROM
employees e
INNER JOIN departments dept using(department_id);
注意:on条件后,只有当左表的字段名和右表字段名一致的时候才能使用using,你像on e.dpetId = d.department_id就不能用using。