从多个表中获取数据:
#错误的实现方式:每个员工都与每个部门匹配了一遍。 SELECT employee_id,department_name FROM employees,departments; #查询出2889条记录 SELECT * FROM employees; #107条记录 SELECT * FROM departments; # 27条记录 SELECT 107*27 FROM dual;
我们把上述多表查询中出现的问题称为:笛卡尔积的错误。
错误的原因:缺少了多表的连接条件
组合的个数即为两个集合中元素个数的乘积数。
交叉连接 ,英文是 CROSS JOIN
。在 SQL99 中也是使用 CROSS JOIN表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。笛卡尔积的错误会在下面条件下产生:
- 省略多个表的连接条件(或关联条件)
- 连接条件(或关联条件)无效
- 所有表中的所有行互相连接
为了避免笛卡尔积, 可以在 WHERE 加入有效的连接条件。
#3、多表查询的正确方式:需要有连接条件(使多个表之间能够相联系起来) SELECT employee_id,department_name FROM employees,departments #两个表的连接条件 WHERE employees.`department_id` = departments.department_id; #4、如果查询语句出现了多个表中都存在的字段,则必须指明此字段所在的表。 #建议:从SQL优化的角度,建议多表查询时,每个字段前都指明其所在的表 。 SELECT employees.employee_id,departments.department_name,employees.department_id FROM employees,departments WHERE employees.`department_id` = departments.department_id; #5、可以给表起别名,在SELECT和WGERE中使用表的别名。 SELECT emp.employee_id,dept.department_name,emp.department_id FROM employees emp,departments dept WHERE emp.`department_id` = dept.department_id; #如果给表起了别名,一旦在SELECT或WHERE中使用表名的话,则必须使用表的别名,而不能再使用表的原名。 #原因:在SQL中,FROM语句最先执行,一旦执行就会把我们自定义表的别名覆盖了表的原名,故不能再使用原名。 #如下的操作是错误的: SELECT emp.employee_id,departments.department_name,emp.department_id FROM employees emp,departments dept WHERE emp.`department_id` = departments.department_id; #6、结论:如果有n个表实现多表的查询,则至少需要n-1个连接条件,否则会出现笛卡尔积的错误。 #练习:查询员工的`employee_id`,`last_name`,`department_name`,`city` SELECT emp.`employee_id`,emp.`last_name`,dept.`department_name`,loc.`city` FROM employees emp,departments dept,locations loc WHERE emp.`department_id` = dept.`department_id` AND dept.`location_id` = loc.`location_id`;
案例
7.1 等值连接 vs 非等值连接 #非等值连接的例子:查询员工`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`;
当table1和table2本质上是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义。然后两个表再进行内连接,外连接等查询。
案例
# 7.2、自连接 vs 非自连接 #自连接的例子: #练习:查询员工id,员工姓名及其管理者的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`;
结果集中不包含一个表与另一个表不匹配的行
返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,
这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。#7.3、内连接 vs 外连接 # 内连接:合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行 SELECT employee_id,department_name FROM employees e,departments d WHERE e.`department_id` = d.department_id; #满足此等式只有106条记录 # 外连接:合并具有同一列的两个以上的表的行, 结果集中除了包含一个表与另一个表匹配的行之外, # 还查询到了左表 或 右表中不匹配的行。 # 外连接的分类:左外连接、右外连接、满外连接 # 左外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回左表中不满足条件的行,这种连接称为左外连接。 # 右外连接:两个表在连接过程中除了返回满足连接条件的行以外还返回右表中不满足条件的行,这种连接称为右外连接。 #练习:查询所有的员工的last_name,department_name信息 (若出现“所有的”字眼,且字段来自不同的表,则一定要使用外连接) SELECT employee_id,department_name FROM employees e,departments d WHERE e.`department_id` = d.department_id; #SQL92语法实现内连接:见上,略 #SQL92语法实现外连接:使用 + ----------MySQL不支持SQL92语法中外连接的写法!Oracle 对 SQL92 支持较好。 #不支持: SELECT employee_id,department_name FROM employees e,departments d WHERE e.`department_id` = d.department_id(+);
使用JOIN…ON子句创建连接的语法结构:
SELECT table1.column, table2.column,table3.column FROM table1 JOIN table2 ON table1 和 table2 的连接条件 JOIN table3 ON table2 和 table3 的连接条件
#SQL99语法中使用 JOIN ...ON 的方式实现多表的查询。这种方式也能解决外连接的问题。MySQL是支持此种方式的。 #SQL99语法如何实现多表的查询。 #语法: SELECT 字段列表 FROM A表 INNER JOIN B表 ON 关联条件 WHERE 等其他子句; #SQL99语法实现内连接:INNER JOIN ... ON (INNER可以省略) SELECT e.last_name,d.department_name FROM employees e INNER JOIN departments d ON e.`department_id` = d.department_id; SELECT e.last_name,d.department_name,l.city FROM employees e JOIN departments d ON e.`department_id` = d.department_id JOIN locations l ON d.`location_id` = l.`location_id`; #连接条件必须是两者都有的,否则查不出任何数据
#SQL99语法实现外连接: #语法: #实现查询结果是A SELECT 字段列表 FROM A表 LEFT JOIN B表 ON 关联条件 WHERE 等其他子句; #练习:查询所有的员工的last_name,department_name信息 #左外连接:LEFT OUTER JOIN ...ON (OUTER可以省略) SELECT last_name,department_name FROM employees e LEFT OUTER JOIN departments d ON e.`department_id` = d.`department_id`;
#语法: #实现查询结果是B SELECT 字段列表 FROM A表 RIGHT JOIN B表 ON 关联条件 WHERE 等其他子句; #右外连接:RIGHT OUTER JOIN ... ON (RIGHT可以省略) SELECT last_name,department_name FROM departments d RIGHT OUTER JOIN employees e ON e.`department_id` = d.`department_id`;
#满外连接:MySQL不支持FULL OUTER JOIN ... ON ,Oracle支持此写法。 SELECT last_name,department_name FROM departments d FULL OUTER JOIN employees e ON e.`department_id` = d.`department_id`;
UNION和UNION ALL的使用
则尽量使用UNION ALL语句,以提高数据查询的效率。
# 7种JOIN的实现: #中图:内连接 SELECT employee_id,department_name FROM employees e JOIN departments d ON e.`department_id` = d.`department_id`; # 左上图:左外连接 SELECT employee_id,department_name FROM employees e LEFT JOIN departments d ON e.`department_id` = d.`department_id`; # 右上图:右外连接 SELECT employee_id,department_name FROM employees e RIGHT JOIN departments d ON e.`department_id` = d.`department_id`; # 左中图: SELECT employee_id,department_name FROM employees e LEFT JOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_id` IS NULL; # 右中图: SELECT employee_id,department_name FROM employees e RIGHT JOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` IS NULL; # 左下图:满外连接 # 方式1:左上图 UNION ALL 右中图 SELECT employee_id,department_name FROM employees e LEFT JOIN departments d ON e.`department_id` = d.`department_id` UNION ALL SELECT employee_id,department_name FROM employees e RIGHT JOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` IS NULL; # 方式2:左中图 UNION ALL 右上图 SELECT employee_id,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,department_name FROM employees e RIGHT JOIN departments d ON e.`department_id` = d.`department_id`; # 右下图:左中图 UNION ALL 右中图 SELECT employee_id,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,department_name FROM employees e RIGHT JOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` IS NULL;
NATURAL JOIN : 它会帮你自动查询两张连接表中所有相同的字段
,然后进行等值连接
。
# SQL99语法的新特性1:自然连接 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`; # NATURAL JOIN : 它会帮你自动查询两张连接表中`所有相同的字段`,然后进行`等值连接`。 SELECT employee_id,last_name,department_name FROM employees e NATURAL JOIN departments d;
当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的 同名字段
进行等值连接。但是只能配合JOIN一起使用。
# SQL99语法的新特性2:USING 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 JOIN departments d USING (department_id);
注意:
我们要 控制连接表的数量 。多表连接就相当于嵌套 for 循环一样,非常消耗资源,会让 SQL 查询性能下降得很严重,因此不要连接不必要的表。在许多 DBMS 中,也都会有最大连接表的限制。
#拓展: SELECT last_name,job_title,department_name FROM employees INNER JOIN departments INNER JOIN jobs ON employees.department_id = departments.department_id AND employees.job_id = jobs.job_id;
# 1.显示所有员工的姓名,部门号和部门名称。 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`; # 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; DESC departments; # 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; #也应该是35条记录 SELECT * FROM employees WHERE commission_pct IS NOT NULL; #35条记录 # 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'; #sql92语法: SELECT e.last_name , e.job_id , e.department_id , d.department_name FROM employees e,departments d ,locations l WHERE e.`department_id` = d.`department_id` AND d.`location_id` = l.`location_id` AND l.`city` = 'Toronto'; # 5.查询员工所在的部门名称、部门地址、姓名、工作、工资,其中员工所在部门的部门名称为’Executive’ SELECT d.department_name,l.street_address,e.last_name,e.job_id,e.salary FROM departments d LEFT JOIN employees e ON e.`department_id` = d.`department_id` LEFT JOIN locations l ON d.`location_id` = l.`location_id` WHERE d.`department_name` = 'Executive'; DESC departments; DESC locations; # 6.选择指定员工的姓名,员工号,以及他的管理者的姓名和员工号,结果类似于下面的格式 employees Emp# manager Mgr# kochhar 101 king 100 SELECT emp.last_name "employees",emp.employee_id "Emp#",mgr.last_name "manager", mgr.employee_id "Mgr#" FROM employees emp LEFT JOIN employees mgr ON emp.manager_id = mgr.employee_id; # 7.查询哪些部门没有员工 SELECT d.department_id FROM departments d LEFT JOIN employees e ON d.`department_id` = e.`department_id` WHERE e.`department_id` IS NULL; #本题也可以使用子查询:暂时不讲 # 8. 查询哪个城市没有部门 SELECT l.location_id,l.city FROM locations l LEFT JOIN departments d ON l.`location_id` = d.`location_id` WHERE d.`location_id` IS NULL; SELECT department_id FROM departments WHERE department_id IN (1000,1100,1200,1300,1600); # 9. 查询部门名为 Sales 或 IT 的员工信息 SELECT e.employee_id,e.last_name,e.department_id FROM employees e JOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_name` IN ('Sales','IT');
以上是MySQL基础篇的多表查询,本章的知识内容来源于尚硅谷MySQL基础,按照视频教学顺序总结出来的。文章总结的若有不足,欢迎大家斧正。谢谢!