目录
一、一个案例引发的多表连接
二、笛卡尔积的错误和与正确的多表查询
2.1、笛卡尔积错误展示
2.2、笛卡尔积解决方法
2.3、练习
三、多表查询分类
3.1、等值连接 vs 非等值连接
3.2、自连接 vs 非自连接
3.3、内连接 vs 外连接
内连接(inner join)
外连接
左外连接(left join)
右外连接(right join)
3.4、七种JOINS查询
左2图
右2图
左下图
右下图
3.5、自然连接(natural join)
3.6、using用法
一、一个案例引发的多表连接
先看这三张表的结构:
员工表的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。