一、多表查询的分类
1、等值连接和非等值连接
(1)等值连接——使用等号进行连接
举例:
# 查询员工的employee_id,last_name,department_name和department_id
# 多个表中有相同列时,必须在列名之前加上表名前缀,同时使用表的别名可以简化查询和提高查询效率
# 如果我们使用了表的别名,在查询字段中、过滤条件中就只能使用别名进行代替,不能使用原有的表名,否则就会报错。
SELECT e.employee_id, e.last_name,d.department_id, d.location_id
FROM employees e , departments d
WHERE e.department_id = d.department_id;
# 查询员工的employee_id,last_name,department_name,city
# 如果有n个表实现多表的查询,则需要至少n-1个连接条件
SELECT e.employee_id,e.last_name,department_name,city
FROM employees e,departments d,locations l
WHERE e.department_id=d.department_id AND d.location_id=l.location_id;# 多个连接条件与 AND 操作符
(2)非等值连接——除了使用等号之外的符号进行连接,包括比较运算符(<,<=,>,>=, BETWEEN)和谓词运算(LIKE, IN, NOT 等)在内的所有的逻辑运算
举例:
# 查询所有员工的last_name,salary和grade_level
SELECT e.last_name,e.salary,j.grade_level
FROM employees e,job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal;
#WHERE e.salary >= j.lowest_sal AND e.salary <= j.highest_sal;
2、自连接和非自连接
(1)自连接——使用自己的表连接自己的表
举例:
# 查询员工id,员工姓名及其管理者的id和姓名
SELECT e.employee_id,e.last_name,m.manager_id,m.last_name
FROM employees e,employees m # 将同一个员工表看成两份,因为员工表中既包含普通员工信息又包含管理者信息
WHERE e.manager_id=m.employee_id
(2)非自连接——使用了多张不同的表(1中的等值连接和非等值连接都是非自连接)
3、内连接和外连接
(1)内连接——合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行
(2)外连接——两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。如果是左外连接,则连接条件中左边的表也称为 主表 ,右边的表称为 从表 ;如果是右外连接,则连接条件中右边的表也称为 主表 ,左边的表称为 从表
注意:在 SQL92 中采用(+)代表从表所在的位置。即左或右外连接中,(+) 表示哪个是从表。在 SQL92 中,只有左外连接和右外连接,没有满(或全)外连接。我们在现实开发场景中一般使用SQL99语法即JOIN…ON…子句实现多表查询。
二、SQL99语法实现多表查询
(1)基本语法
# 可以使用 ON 子句指定额外的连接条件,此连接条件可以与其他条件分开
# ON子句使语句具有更高的易读性
# 关键字 JOIN、INNER JOIN、CROSS JOIN 的含义是一样的,都表示内连接
SELECT table1.column, table2.column,table3.column
FROM table1
JOIN table2 ON table1 和 table2 的连接条件
JOIN table3 ON table2 和 table3 的连接条件
(2)内连接(INNER JOIN)的实现
语法:
SELECT 字段列表
FROM A表 INNER JOIN B表
ON 关联条件
WHERE 等其他子句;
举例:
# 查询员工的employee_id,last_name,department_id,department_name和所在部门的location_id
# 关键字 JOIN、INNER JOIN、CROSS JOIN 的含义是一样的,都表示内连接
# 方式一
SELECT e.employee_id,e.last_name,e.department_id,
d.department_id,d.location_id
FROM employees e JOIN departments d
ON e.department_id=d.department_id;
# 方式二
SELECT e.employee_id,e.last_name,e.department_id,
d.department_id,d.location_id
FROM employees e INNER JOIN departments d
ON e.department_id=d.department_id;
# 方式三
SELECT e.employee_id,e.last_name,e.department_id,
d.department_id,d.location_id
FROM employees e CROSS JOIN departments d
ON e.department_id=d.department_id;
# 方式四(SQL92语法实现内连接)
SELECT e.employee_id,e.last_name,e.department_id,
d.department_id,d.location_id
FROM employees e,departments d
WHERE e.department_id=d.department_id;
# 查询员工的employee_id,department_name和部门所在的城市city
SELECT employee_id,department_name,city
FROM employees e
JOIN departments d ON e.department_id=d.department_id
JOIN locations l ON d.location_id=l.location_id;
(3)外连接(OUTER JOIN)的实现(看见题目中的“所有”二字要敏感,可能使用外连接,且左外或右外连接中的左右只是逻辑上的左右。调换顺序,左外连接可以变成右外连接,右外连接可以变成左外连接)
I、左外连接
语法:
#实现查询结果是A
SELECT 字段列表
#FROM A表 LEFT OUTER JOIN B表 (OUTER可以省略不写)
FROM A表 LEFT JOIN B表
ON 关联条件
WHERE 等其他子句;
举例:
# 查询所有员工的last_name,department_id和department_name
# 方式一
SELECT e.last_name,e.department_id,d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id=d.department_id;
# 方式二
SELECT e.last_name,e.department_id,d.department_name
FROM employees e
LEFT OUTER JOIN departments d ON e.department_id=d.department_id;
语法:
#实现查询结果是B
SELECT 字段列表
# FROM A表 RIGHT OUTER JOIN B表 (OUTER可以省略不写)
FROM A表 RIGHT JOIN B表
ON 关联条件
WHERE 等其他子句;
举例:
# 查询所有员工的last_name,department_id和department_name
# 方式一
SELECT e.last_name,e.department_id,d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id=d.department_id;
# 方式二
SELECT e.last_name,e.department_id,d.department_name
FROM employees e
RIGHT OUTER JOIN departments d ON e.department_id=d.department_id;
III、满外连接
1、满外连接的结果 = 左右表匹配的数据 + 左表没有匹配到的数据 + 右表没有匹配到的数据。
2、SQL99是支持满外连接的。使用FULL JOIN 或 FULL OUTER JOIN来实现。
3、MySQL不支持FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT join代替。
IV、左外连接和右外连接的区别
三、UNION的使用
(1)利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并
时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION或UNION ALL关键字分隔。
语法:
SELECT column,... FROM tableA
UNION [ALL]
SELECT column,... FROM tableB
(2)UNION操作符——返回两个查询的结果集的并集,去除重复记录
(3)UNION ALL操作符——返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重
注意:
执行UNION ALL语句时所需要的资源比UNION语句少。如果明确知道合并数据后的结果数据
不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL语句,以提高数据查询的效 率。
举例:
# 查询部门编号>90或邮箱包含a的员工信息
# 方式一
SELECT *
FROM employees
WHERE email LIKE '%a%' OR department_id>90;
# 方式二
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id>90;
#中图:内连接 A∩B
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d ON e.department_id= d.department_id;
#左上图:左外连接
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id;
#右上图:右外连接
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d ON e.department_id= d.department_id;
#左中图:A - A∩B
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id
WHERE d.department_id IS NULL;
#右中图:B-A∩B
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE e.department_id IS NULL;
#左下图:满外连接
# 左中图 + 右上图 A∪B
SELECT employee_id,last_name,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,last_name,department_name
FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id;
#右下图
#左中图 + 右中图 A ∪B- A∩B 或者 (A - A∩B) ∪ (B - A∩B)
SELECT employee_id,last_name,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,last_name,department_name FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE e.department_id IS NULL
(1)自然连接
SQL99 在 SQL92 的基础上提供了一些特殊语法,比如 NATURAL JOIN 用来表示自然连接。我们可以把自然连接理解为 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`;
# 方式二
SELECT employee_id,last_name,department_name
FROM employees e NATURAL JOIN departments d;
当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的同名字段进行等值连接(不适用于自连接)。但是只能配合JOIN一起使用。与自然连接 NATURAL JOIN 不同的是,USING 指定了具体的相同的字段名称,你需要在 USING的括号 () 中填入要指定的同名字段。同时使用 JOIN…USING 可以简化 JOIN ON 的等值连接。
举例:
# 以下两种查询方式都一样
# 方式一
SELECT employee_id,last_name,department_name
FROM employees e ,departments d
WHERE e.department_id = d.department_id;
# 方式二
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
USING (department_id);
(1)表连接的约束条件可以有三种方式:WHERE(SQL92语法), ON(SQL99语法), USING(SQL99语法)。
WHERE:适用于所有关联查询。
ON :只能和JOIN一起使用,只能写关联条件。虽然关联条件可以并到WHERE中和其他条件一起写,但分开写可读性更好。
USING:只能和JOIN一起使用,而且要求两个关联字段在关联表中名称一致,而且只能表示关联字 段值相等。(条件苛刻)
(2)我们要控制连接表的数量 。多表连接就相当于嵌套 for 循环一样,非常消耗资源,会让 SQL 查询性能下
降得很严重,因此不要连接不必要的表。在许多 DBMS 中,也都会有最大连接表的限制。
七、小练习
# 1.显示所有员工的姓名,部门号和部门名称。
SELECT 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 e.job_id,d.location_id
FROM employees e JOIN departments d
ON e.department_id=d.department_id
WHERE d.department_id=90;
# 3.选择所有有奖金的员工的 last_name , department_name , location_id , city
SELECT e.last_name,e.commission_pct,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
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.last_name,e.job_id,e.salary
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 d.department_name='Executive';
# 6.选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式
# employees Emp# manager
# 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.查询哪些部门没有员工 (想象成两个集合:部门表和员工表)
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.location_id
FROM locations l LEFT JOIN departments d
ON l.location_id=d.location_id
WHERE d.department_id IS NULL;
# 9. 查询部门名为 Sales 或 IT 的员工信息
SELECT *
FROM employees e JOIN departments d
ON e.department_id=d.department_id
#WHERE d.department_name='Sales' OR d.department_name='IT';
WHERE d.department_name IN ('Sales','IT');
所用数据库请看文章末尾:SQL语言和基本的select语句