七MySQL 多表查询详解(附练习题及答案----超详细)

Posted Amo Xiang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了七MySQL 多表查询详解(附练习题及答案----超详细)相关的知识,希望对你有一定的参考价值。

文章目录

连接是关系数据库模型的主要特点。连接查询是关系数据库中最主要的查询,主要包括内连接、外连接等。通过连接运算符可以实现多个表查询 (故也称为多表查询)。 在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在一个表中。在查询数据时,通过连接操作查询出存放在多个表中的不同实体的信息。当两个或多个表中存在相同意义的字段时,便可以通过这些字段对不同的表进行连接查询。本文将介绍多表之间的内连接查询、外连接查询以及复合条件连接查询。

前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个 关联字段可能建立了外键,也可能没有建立外键。比如:员工表和部门表,这两个表依靠 部门编号 进行关联。

前置知识:

一、数据库开发与实战专栏导学及数据库基础概念入门
二、MySQL 介绍及 MySQL 安装与配置
三、MySQL 数据库的基本操作
四、MySQL 存储引擎及数据类型
五、数据导入与基本的 SELECT 语句
六、MySQL 数据库练习题1(包含前5章练习题目及答案)

一、笛卡尔积(或交叉连接)的理解

笛卡尔积(Cartesian product)是指两个集合 X 和 Y 的乘积。例如,有 A 和 B 两个集合,它们的值如下:

A = 1,2
B = 3,4,5

集合 A×B 和 B×A 的结果集分别表示为:

A×B=(1,3), (1,4), (1,5), (2,3), (2,4), (2,5) ;
B×A=(3,1), (3,2), (4,1), (4,2), (5,1), (5,2) ;

以上 A×B 和 B×A 的结果就叫做两个集合的笛卡尔积。并且,从以上结果我们可以看出:

  1. 两个集合相乘,不满足交换率,即 A×B≠B×A。
  2. A 集合和 B 集合的笛卡尔积是 A 集合的元素个数 × B 集合的元素个数。

图示:

SQL92中,笛卡尔积也称为交叉连接 ,英文是 CROSS JOIN 。在 SQL99 中也是使用 CROSS JOIN 表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。在 mysql 中如下情况会出现笛卡尔积:

#查询员工姓名和所在部门名称 但是根据查询结果来看 是错误的
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;

笛卡尔积的错误会在下面条件下产生:

省略多个表的连接条件(或关联条件)
连接条件(或关联条件)无效
所有表中的所有行互相连接

为了避免笛卡尔积的错误, 可以在 WHERE 加入有效的连接条件。 交叉连接的语法格式如下:

#语法格式1
SELECT <字段名> FROM <1> CROSS JOIN <2> [WHERE子句]
#语法格式2
SELECT <字段名> FROM <1>, <2> [WHERE子句] 
#字段名:需要查询的字段名称。在表中有相同列时,在列名之前加上表名前缀
#<表1><表2>:需要交叉连接的表名
#WHERE 子句:用来设置交叉连接的查询条件

注意: 多个表交叉连接时,在 FROM 后连续使用 CROSS JOIN, 即可。以上两种语法的返回结果是相同的,但是第一种语法才是官方建议的标准写法。当连接的表之间没有关系时,我们会省略掉 WHERE 子句,这时返回结果就是两个表的笛卡尔积,返回结果数量就是两个表的数据行相乘。需要注意的是,如果每个表有 1000 行,那么返回结果的数量就有 1000×1000 = 1000000 行,数据量是非常巨大的,就是上述所说的笛卡尔积错误,得到的运行结果没太大的意义。

【示例1】查询员工姓名和所在部门名称(正确写法)。

SELECT emp.last_name,dep.department_name FROM employees emp,departments dep WHERE emp.department_id=dep.department_id;
SELECT emp.last_name,dep.department_name FROM employees emp CROSS JOIN departments dep ON emp.department_id=dep.department_id;

如果在交叉连接时使用 WHERE 子句,MySQL 会先生成两个表的笛卡尔积,然后再选择满足 WHERE 条件的记录。因此,表的数量较多时,交叉连接会非常非常慢。一般情况下不建议使用交叉连接。在 MySQL 中,多表查询一般使用内连接和外连接,它们的效率要高于交叉连接。

二、多表查询分类讲解

2.1 分类1:等值连接 vs 非等值连接

等值连接:

SQL 语句如下所示:

mysql> SELECT emp.last_name,dep.department_name,dep.location_id
    -> FROM employees emp,departments dep
    -> WHERE emp.department_id=dep.department_id;

【示例2】查询员工姓名和所在部门名称(正确写法)。多个连接条件与 AND 操作符:

#1.区分重复的列名 多个表中有相同列时,必须在列名之前加上表名前缀
#2.使用别名可以简化查询 列名前使用表名前缀可以提高查询效率
SELECT emp.last_name,emp.department_id,dep.department_name,dep.location_id 
FROM employees emp,departments dep 
WHERE emp.department_id=dep.department_id AND dep.department_id=50;
#3.需要注意的是,如果我们使用了表的别名,在查询字段中、过滤条件中就只能使用别名进行代替,使用原表名会报错!

【示例3】连接多个表:查询出公司员工的 last_name、department_name、city。

mysql> SELECT emp.last_name,emp.department_id,dep.department_name,dep.location_id, loc.city
    -> FROM employees emp,departments dep,locations loc
    -> WHERE emp.department_id=dep.department_id AND dep.location_id=loc.location_id;

非等值连接:

【示例4】非等值连接。使用sql语句实现上图所示查询结果。

mysql> SELECT emp.last_name,emp.salary,gra.grade_level,gra.lowest_sal,gra.highest_sal
    -> FROM employees emp,job_grades gra
    -> WHERE emp.salary BETWEEN gra.lowest_sal AND gra.highest_sal;

2.2 分类2:自连接 vs 非自连接

【示例5】查询出员工的 manager 的信息。

mysql> SELECT
    -> emp1.employee_id,emp1.last_name,emp1.manager_id,
    -> emp2.employee_id,emp2.last_name
    -> FROM employees emp1, employees emp2
    -> WHERE emp1.manager_id=emp2.employee_id;

查询结果如下图所示:

emp1 和 emp2 本质上是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义。然后两个表再进行内连接,外连接等查询。【示例6】查询出 last_name为 Chen 的员工的 manager 的信息。

mysql> SELECT
    -> emp1.employee_id,emp1.last_name,emp1.manager_id,
    -> emp2.employee_id,emp2.last_name
    -> FROM employees emp1, employees emp2
    -> WHERE emp1.manager_id=emp2.employee_id AND emp1.last_name='Chen';
+-------------+-----------+------------+-------------+-----------+
| employee_id | last_name | manager_id | employee_id | last_name |
+-------------+-----------+------------+-------------+-----------+
|         110 | Chen      |        108 |         108 | Greenberg |
+-------------+-----------+------------+-------------+-----------+
1 row in set (0.00 sec)

2.3 分类3:内连接 vs 外连接

内连接(INNER JOIN): 使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行,组合成新的记录,也就是说,在内连接查询中,只有满足条件的记录才能出现在结果关系中。

连接查询将查询多个表中相关联的行;内连接时,返回查询结果集合中仅是符合查询条件和连接条件的行。有时候需要包含没有关联的行中数据,如下图所示:

如上图所示, 返回查询结果集合中不仅包含符合连接条件的行,还包括 左表(左外连接或左连接)[右表(右外连接或右连接)或两个边接表(全外连接)] 中的所有数据行。外连接分为左外连接(左连接)和右外连接(右连接):

  1. LEFT JOIN(左连接):返回包括左表中的所有记录和右表中连接字段相等的记录。左连接的结果包括 LEFT OUTER 子句中指定的左表的所有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果行中,右表的所有选择列表列均为空值。连接条件中左边的表也称为 主表 ,右边的表称为 从表
  2. RIGHT JOIN(右连接):返回包括右表中的所有记录和左表中连接字段相等的记录。右连接是左连接的反向连接,将返回右表的所有行。如果右表的某行在左表中没有匹配行,左表将返回空值。如果是右外连接,则连接条件中右边的表也称为 主表 ,左边的表称为 从表

2.4 SQL99语法实现多表查询

2.4.1 内连接

内连接的语法格式如下:

SELECT <字段名> FROM <1> INNER JOIN <2> [ON子句]
#字段名:需要查询的字段名称。
#<表1><表2>:需要内连接的表名。
#INNER JOIN :内连接中可以省略 INNER 关键字,只用关键字 JOIN。
#ON 子句:用来设置内连接的连接条件。

多个表内连接时,在 FROM 后连续使用 INNER JOIN 或 JOIN 即可。

INNER JOIN 也可以使用 WHERE 子句指定连接条件,但是 INNER JOIN … ON 语法是官方的标准写法,而且 WHERE 子句在某些时候会影响查询的性能。

【示例7】查询员工信息及其部门信息。

mysql> SELECT
    -> emp1.employee_id,emp1.last_name,emp1.department_id,
    -> dep.department_id,dep.location_id
    -> FROM employees emp1 INNER JOIN departments dep
    -> ON emp1.department_id=dep.department_id;

【示例8】查询出员工的 manager 的信息。

mysql> SELECT
    -> emp1.employee_id,emp1.last_name,emp1.manager_id,
    -> emp2.employee_id,emp2.last_name
    -> FROM employees emp1 INNER JOIN employees emp2
    -> ON emp1.manager_id=emp2.employee_id;

【示例9】多表连接三张表。

mysql> SELECT
    -> emp1.employee_id,emp1.last_name,emp1.department_id,
    -> dep.department_id,dep.location_id,loc.city,loc.state_province
    -> FROM employees emp1 INNER JOIN departments dep
    -> ON emp1.department_id=dep.department_id
    -> INNER JOIN locations loc ON dep.location_id = loc.location_id;

2.4.2 左连接

左外连接又称为左连接,使用 LEFT OUTER JOIN 关键字连接两个表,并使用 ON 子句来设置连接条件。左连接的语法格式如下:

SELECT <字段名> FROM <1> LEFT OUTER JOIN <2> <ON子句>
#字段名:需要查询的字段名称。
#<表1><表2>:需要左连接的表名。
#LEFT OUTER JOIN:左连接中可以省略 OUTER 关键字,只使用关键字 LEFT JOIN。
#ON 子句:用来设置左连接的连接条件,不能省略。

上述语法中,表1 为主表,表2 为从表。左连接查询时,可以查询出 表1 中的所有记录和 表2 中匹配连接条件的记录。如果 表1 的某行在 表2 中没有匹配行,那么在返回结果中,表2 的字段值均为 空值(NULL), 在之前已经提过。

mysql> SELECT
    ->   dep.department_id,dep.location_id,
    -> emp.employee_id,emp.last_name,emp.department_id
    -> FROM departments dep LEFT OUTER JOIN employees emp
    -> ON dep.department_id=emp.department_id;

2.4.3 右连接

右外连接又称为右连接,右连接是左连接的反向连接。使用 RIGHT OUTER JOIN 关键字连接两个表,并使用 ON 子句来设置连接条件。右连接的语法格式如下:

SELECT <字段名> FROM <1> RIGHT OUTER JOIN <2> <ON子句>
#字段名:需要查询的字段名称。
#<表1><表2>:需要右连接的表名。
#RIGHT OUTER JOIN:右连接中可以省略 OUTER 关键字,只使用关键字 RIGHT JOIN。
#ON 子句:用来设置右连接的连接条件,不能省略。

与左连接相反,右连接以 表2 为主表,表1 为从表。右连接查询时,可以查询出 表2 中的所有记录和 表1 中匹配连接条件的记录。如果 表2 的某行在 表1 中没有匹配行,那么在返回结果中,表1 的字段值均为空值(NULL)。

mysql> SELECT
    ->   dep.department_id,dep.location_id,
    -> emp.employee_id,emp.last_name,emp.department_id
    -> FROM departments dep RIGHT OUTER JOIN employees emp
    -> ON dep.department_id=emp.department_id;

多个表左/右连接时,在 ON 子句后连续使用 LEFT/RIGHT OUTER JOIN 或 LEFT/RIGHT JOIN 即可。使用外连接查询时,一定要分清需要查询的结果,是需要显示左表的全部记录还是右表的全部记录,然后选择相应的左连接和右连接。

2.4.4 满外连接(FULL OUTER JOIN)

满外连接的结果 = 左右表匹配的数据 + 左表没有匹配到的数据 + 右表没有匹配到的数据。
SQL99是支持满外连接的。使用 FULL JOIN 或 FULL OUTER JOIN 来实现。需要注意的是,MySQL 不支持 FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT JOIN 代替。

2.4.5 补充:合并查询结果 UNION 使用

利用 UNION 关键字,可以给出多条 SELECT 语句,并将它们的结果组合成单个结果集。合并时,两个表对应的列数和数据类型必须相同。各个 SELECT 语句之间使用 UNION 或 UNION ALL 关键字分隔。UNION 不使用关键字 ALL,执行的时候删除重复的记录,所有返回的行都是唯一的;使用关键字 ALL 的作用是不删除重复行也不对结果进行自动排序。基本语法格式如下:

SELECT column,... FROM table1
UNION [ALL]
SELECT column,... FROM table2

注意: 执行 UNION ALL 语句时所需要的资源比 UNION 语句少。如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,则尽量使用 UNION ALL 语句,以提高数据查询的效率。【示例10】查询部门编号>90或邮箱包含a的员工信息。

mysql> SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;
mysql> SELECT * FROM employees WHERE email LIKE '%a%' UNION
    -> SELECT * FROM employees WHERE department_id>90;
mysql> SELECT * FROM employees WHERE email LIKE '%a%' UNION ALL#有重复数据
    -> SELECT * FROM employees WHERE department_id>90;

2.5 7种 SQL JOINS 的实现


sql 示例:

mysql> SELECT employee_id,last_name,department_name FROM employees e LEFT JOIN departments d
    -> ON e.`department_id` = d.`department_id`;

右连接:

sql 示例:

mysql> SELECT employee_id,last_name,department_name FROM employees e RIGHT JOIN departments d
    -> ON e.`department_id` = d.`department_id`;

内连接:

sql 示例:

mysql> SELECT employee_id,last_name,department_name FROM employees e INNER JOIN departments d
    -> ON e.`department_id` = d.`department_id`;

全连接:

sql 示例:

mysql> 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`;

剩下的三个图:

sql 示例:

mysql> #左图
mysql> 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
    -> ;
+-------------+-----------+-----------------+
| employee_id | last_name | department_name |
+-------------+-----------+-----------------+
|         178 | Grant     | NULL            |
+-------------+-----------+-----------------+
1 row in set (0.00 sec)

mysql> #右图
mysql> 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;
+-------------+-----------+----------------------+
| employee_id | last_name | department_name      |
+-------------+-----------+----------------------+
|        NULL | NULL      | Treasury             |
|        NULL | NULL      | Corporate Tax        |
|        NULL | NULL      | Control And Credit   |
|        NULL | NULL      | Shareholder Services |
|        NULL | NULL      | Benefits             |
|        NULL | NULL      | Manufacturing        |
|        NULL | NULL      | Construction         |
|        NULL | NULL      | Contracting          |
|        NULL | NULL      | Operations           |
|        NULL | NULL      | IT Support           |
|        NULL | NULL      | NOC                  |
|        NULL | NULL      | IT Helpdesk          |
|        NULL | NULL      | Government Sales     |
|        NULL | NULL      | Retail Sales         |
|        NULL | NULL      | Recruiting           |
|        NULL | NULL      | Payroll              |
+-------------+-----------+----------------------+
16 rows in set (0.00 sec)

mysql> #中图
mysql> 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;
+-------------+-----------+----------------------+
| employee_id | last_name | department_name      |
+-------------+-----------+----------------------+
|         178 | Grant     | NULL                 |
|        NULL | NULL      | Treasury             |
|        NULL | NULL      | Corporate Tax        |
|        NULL | NULL      | Control And Credit   |
|        NULL | NULL      | Shareholder Services |
|        NULL | NULL      | Benefits             |
|        NULL | NULL      | Manufacturing        |
|        NULL | NULL      | Construction         |
|        NULL | NULL      | Contracting          |
|        NULL | NULL      | Operations           |
|        NULL | NULL      | IT Support           |
|        NULL | NULL      | NOC                  |
|        NULL | NULL      | IT Helpdesk          |
|        NULL | NULL      | Government Sales     |
|        NULL | NULL      | Retail Sales         |
|        NULL | NULL      | Recruiting           |
|        NULL | NULL      | Payroll              |
+-------------+-----------+----------------------+
17 rows in set (0.00 sec)

2.6 SQL99语法新特性

2.6.1 自然连接

SQL99 在 SQL92 的基础上提供了一些特殊语法,比如 NATURAL JOIN 用来表示自然连接。我们可以把自然连接理解为 SQL92 中的等值连接。它会帮你自动查询两张连接表中 所有相同的字段,然后进行 等值连接。在 SQL92 标准中:

mysql> SELECT employee_id,last_name,department_name FROM employees e Mysql笛卡尔积详解(附实现多表查询代码实现)

MySQL多表查询练习题

数据库2.0

基础训练|七年级语文选择排序题专项练习(附答案)

MySQL多表查询

MySQL练习题及答案