目录
1. 一个案例引发的多表连接
1.1 案例说明
1.2 笛卡尔积(或交叉连接)的理解
1.3 案例分析与问题解决
2. 多表查询分类讲解
分类1:等值连接 vs 非等值连接
等值连接
非等值连接
编辑
分类2:自连接 vs 非自连接
分类3:内连接 vs 外连接
3. SQL99语法实现多表查询
3.2 内连接(INNER JOIN)的实现
3.3 外连接(OUTER JOIN)的实现
3.3.1 左外连接(LEFT OUTER JOIN)
3.3.2 右外连接(RIGHT OUTER JOIN)
3.3.3 满外连接(FULL OUTER JOIN)
4. UNION的使用
5. 7种SQL JOINS的实现
5.7.1 代码实现
5.7.2 语法格式小结
6. SQL99语法新特性
6.1 自然连接
6.2 USING连接
7. 章节小结
多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。
从多个表中获取数据
# 案例:查询员工的姓名及其部门名称SELECT last_name, department_nameFROM employees, departments;
分析错误情况:
SELECT COUNT (employee_id) FROM employees;# 输出 107 行SELECT COUNT (department_id) FROM departments;# 输出 27 行SELECT 107 * 27 FROM dual ;
我们把上述多表查询中出现的问题称为:笛卡尔积的错误
# 查询员工姓名和所在部门名称SELECT last_name,department_name FROM employees,departments;SELECT last_name,department_name FROM employees CROSS JOIN departments;SELECT last_name,department_name FROM employees INNER JOIN departments;SELECT last_name,department_name FROM employees JOIN departments;
省略多个表的连接条件(或关联条件)连接条件(或关联条件)无效所有表中的所有行互相连接
SELECT table1 .column , table2 .columnFROM table1, table2WHERE table1 .column 1 = table2 .column 2 ; # 连接条件
# 案例:查询员工的姓名及其部门名称SELECT last_name, department_nameFROM employees, departmentsWHERE employees .department_id = departments .department_id ;
SELECT employees.employee_id, employees.last_name,
employees .department_id , departments .department_id ,departments .location_idFROM employees, departmentsWHERE employees .department_id = departments .department_id ;
拓展1:多个连接条件与 AND 操作符
拓展2:区分重复的列名
SELECT employees .last_name , departments .department_name ,employees .department_idFROM employees, departmentsWHERE employees .department_id = departments .department_id ;
拓展3:表的别名
SELECT e .employee_id , e .last_name , e .department_id ,d .department_id , d .location_idFROM employees e , departments dWHERE e .department_id = d .department_id ;
阿里开发规范 :【 强制 】对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定。说明 :对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),并且操作列在多个表中存在时,就会抛异常。正例 : select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id;反例 :在某业务中,由于多表关联查询语句没有加表的别名(或表名)的限制,正常运行两年后,最近在 某个表中增加一个同名字段,在预发布环境做数据库变更后,线上查询语句出现出 1052 异常: Column 'name' in field list is ambiguous 。
拓展4:连接多个表
SELECT e .last_name , e .salary , j .grade_levelFROM employees e, job_grades jWHERE e .salary BETWEEN j .lowest_sal AND j .highest_sal ;
SELECT CONCAT(worker .last_name , ' works for ', manager .last_name )FROM employees worker, employees managerWHERE worker .manager_id = manager .employee_id ;
练习:查询出last_name为 ‘Chen’ 的员工的 manager 的信息。
# 左外连接SELECT last_name,department_nameFROM employees ,departmentsWHERE employees .department_id = departments .department_id (+);# 右外连接SELECT last_name,department_nameFROM employees ,departmentsWHERE employees .department_id (+) = departments .department_id ;
SELECT table1 .column , table2 .column ,table3 .columnFROM table1JOIN table2 ON table1 和 table2 的连接条件JOIN table3 ON table2 和 table3 的连接条件
for t1 in table1:for t2 in table2:if condition1:for t3 in table3:if condition2:output t1 + t2 + t3
可以使用 ON 子句指定额外的连接条件 。这个连接条件是与其它条件分开的。ON 子句使语句具有更高的易读性 。关键字 JOIN 、 INNER JOIN 、 CROSS JOIN 的含义是一样的,都表示内连接
SELECT 字段列表FROM A 表 INNER JOIN B 表ON 关联条件WHERE 等其他子句 ;
SELECT e .employee_id , e .last_name , e .department_id ,d .department_id , d .location_idFROM employees e JOIN departments dON (e .department_id = d .department_id );
SELECT employee_id, city, department_nameFROM employees eJOIN departments dON d .department_id = e .department_idJOIN locations lON d .location_id = l .location_id ;
# 实现查询结果是 ASELECT 字段列表FROM A 表 LEFT JOIN B 表ON 关联条件WHERE 等其他子句 ;
举例
SELECT e .last_name , e .department_id , d .department_nameFROM employees eLEFT OUTER JOIN departments dON (e .department_id = d .department_id ) ;
# 实现查询结果是 BSELECT 字段列表FROM A 表 RIGHT JOIN B 表ON 关联条件WHERE 等其他子句 ;
举例:
SELECT e .last_name , e .department_id , d .department_nameFROM employees eRIGHT OUTER JOIN departments dON (e .department_id = d .department_id ) ;
SELECT column ,... FROM table1UNION [ ALL ]SELECT column ,... FROM table2
UNION操作符
举例:查询部门编号>90或邮箱包含a的员工信息
# 方式 1SELECT * FROM employees WHERE email LIKE '%a%' OR department_id> 90 ;
举例:查询中国用户中男性的信息以及美国用户中年男性的用户信息
SELECT id,cname FROM t_chinamale WHERE csex= ' 男 'UNION ALLSELECT id,tname FROM t_usmale WHERE tGender= 'male' ;
# 中图:内连接 A ∩ BSELECT employee_id,last_name,department_nameFROM employees e JOIN departments dON e. `department_id` = d. `department_id` ;
# 左上图:左外连接SELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e. `department_id` = d. `department_id` ;
# 右上图:右外连接SELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e. `department_id` = d. `department_id` ;
# 左中图: A - A ∩ BSELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e. `department_id` = d. `department_id`WHERE d. `department_id` IS NULL
# 右中图: B-A ∩ BSELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e. `department_id` = d. `department_id`WHERE e. `department_id` IS NULL
# 左下图:满外连接# 左中图 + 右上图 A ∪ BSELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e. `department_id` = d. `department_id`WHERE d. `department_id` IS NULLUNION ALL # 没有去重操作,效率高SELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e. `department_id` = d. `department_id` ;
# 右下图# 左中图 + 右中图 A ∪ B- A ∩ B 或者 (A - A ∩ B) ∪ ( B - A ∩ B )SELECT employee_id,last_name,department_nameFROM employees e LEFT JOIN departments dON e. `department_id` = d. `department_id`WHERE d. `department_id` IS NULLUNION ALLSELECT employee_id,last_name,department_nameFROM employees e RIGHT JOIN departments dON e. `department_id` = d. `department_id`WHERE e. `department_id` IS NULL
# 实现 A - A ∩ Bselect 字段列表from A 表 left join B 表on 关联条件where 从表关联字段 is null and 等其他子句 ;
# 实现 B - A ∩ Bselect 字段列表from A 表 right join B 表on 关联条件where 从表关联字段 is null and 等其他子句 ;
# 实现查询结果是 A ∪ B# 用左外的 A , union 右外的 Bselect 字段列表from A 表 left join B 表on 关联条件where 等其他子句unionselect 字段列表from A 表 right join B 表on 关联条件where 等其他子句 ;
右下图
#实现A∪B - A∩B 或 (A - A∩B) ∪ (B - A∩B)
# 使用左外的 (A - A ∩ B) union 右外的( B - A ∩ B )select 字段列表from A 表 left join B 表on 关联条件where 从表关联字段 is null and 等其他子句unionselect 字段列表from A 表 right join B 表on 关联条件where 从表关联字段 is null and 等其他子句
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments dON e. `department_id` = d. `department_id`AND e. `manager_id` = d. `manager_id` ;
在 SQL99 中你可以写成
SELECT employee_id,last_name,department_nameFROM employees e NATURAL JOIN departments d;
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 ;
# 关联条件# 把关联条件写在 where 后面SELECT last_name,department_nameFROM employees,departmentsWHERE employees .department_id = departments .department_id ;# 把关联条件写在 on 后面,只能和 JOIN 一起使用SELECT last_name,department_nameFROM employees INNER JOIN departmentsON employees .department_id = departments .department_id ;SELECT last_name,department_nameFROM employees CROSS JOIN departmentsON employees .department_id = departments .department_id ;SELECT last_name,department_nameFROM employees JOIN departmentsON employees .department_id = departments .department_id ;# 把关联字段写在 using() 中,只能和 JOIN 一起使用# 而且两个表中的关联字段必须名称相同,而且只能表示 =# 查询员工姓名与基本工资SELECT last_name,job_titleFROM employees INNER JOIN jobs USING (job_id);#n 张表关联,需要 n-1 个关联条件# 查询员工姓名,基本工资,部门名称SELECT last_name,job_title,department_name FROM employees,departments,jobsWHERE employees .department_id = departments .department_idAND employees .job_id = jobs .job_id ;SELECT last_name,job_title,department_nameFROM employees INNER JOIN departments INNER JOIN jobsON employees .department_id = departments .department_idAND employees .job_id = jobs .job_id ;