【MySQL】多表查询

文章目录

  • 多表查询是什么?
  • 一、笛卡尔积(或交叉连接)
  • 二、多表查询分类
    • 分类1:等值连接 vs 非等值连接
      • 等值连接
      • 非等值连接
    • 分类2:自连接 vs 非自连接
    • 分类2:内连接 vs 外连接
  • 三、SQL99
    • SQL99 实现 内连接
    • SQL99 实现 外连接(OUTER JOIN)
      • 左外连接(LEFT OUTER JOIN)
      • 右外连接(RIGHT OUTER JOIN)
      • 满外连接(FULL OUTER JOIN)
    • UNION 和 UNION ALL的使用
  • 四、7种SQL JOINS的实现
    • 1. 内连接
    • 2. 左外连接
    • 3. 右外连接
    • 4.左外连接 但查NULL(A - A∩B)
    • 5. 右外联接 但查Null(B - A∩B)
    • 6.满外连接 A∪B
    • 7. 满外连接 但是查的是Null -> (A - A∩B) ∪ (B - A∩B)
  • 五、SQL99 语法新特性
    • 自然连接 NATURAL
    • USING连接
  • 总结
  • 综合练习


多表查询是什么?

也称为关联查询,指两个或更多个表一起完成查询操作。

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

以下错误的实现方式:

#案例:查询员工的姓名及其部门名称
SELECT last_name, department_name
FROM employees, departments;

jieguo
总共查出:2889条数据
但是:
错误
107 * 27 == 2889 也就是 每个员工和每一个部门都匹配了 一次

以上为:笛卡尔积的错误


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

笛卡尔积也称为交叉连接,英文是 CROSS JOIN
作用就是可以把任意表进行连接,即使这两张表不相关。

针对以上错误案例的分析: 为了避免笛卡尔积, 可以在 WHERE 加入有效的连接条件。
语法变为:

SELECT	table1.column, table2.column
FROM	table1, table2
WHERE	table1.column1 = table2.column2;  #连接条件

正确案例:

SELECT last_name,department_name
FROM employees,departments
WHERE employees.department_id = departments.department_id;

【MySQL】多表查询_第1张图片

二、多表查询分类

【MySQL】多表查询_第2张图片

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

等值连接

1.列名加前缀
在表中有相同列时,在列名之前加上表名前缀(也就是把两张表的ID关联起来)
效果
如果查询语句中出现了多个表中都存在的字段,则必须指明此字段所在的表。

SELECT employees.last_name,departments.department_name,departments.department_id
FROM employees,departments
WHERE employees.department_id = departments.department_id;

建议:从sql优化的角度,建议多表查询时,每个字段前都指明其所在的表。


2.表起别名
也可以给表起别名,如果给表起了别名,一旦在SELECT或WHERE中使用表名的话,则必须使用表的别名,而不能再使用表的原名。

SELECT e.last_name,d.department_name,d.department_id
FROM employees e,departments d
WHERE e.department_id = d.department_id;

3.连接多个表 用AND
如果有n个表实现多表的查询,则需要至少n-1个连接条件

#练习:查询员工的employee_id,last_name,department_name,city
SELECT e.last_name,d.department_name,l.city,e.department_id,l.location_id
FROM employees e,departments d,locations l
WHERE e.department_id = d.department_id
AND d.location_id = l.location_id;

效果

非等值连接

【MySQL】多表查询_第3张图片

#标出员工表中的员工工资 在工作等级表中对应的薪资等级
SELECT e.employee_id,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;

【MySQL】多表查询_第4张图片

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

【MySQL】多表查询_第5张图片
当table1和table2本质上是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义。
然后两个表再进行内连接,外连接等查询。
(同一张表,不同的查询条件)
【MySQL】多表查询_第6张图片
emp表的管理员ID 对应 员工ID 所以 使用mgr表中的员工与其关联!

# 员工表员工ID 与 员工表管理ID 是同一个 通过where 关联ID
-- CONCAT 合并函数 --
SELECT CONCAT(e.last_name,' work for ',m.last_name)
FROM employees e,employees m
WHERE e.manager_id = m.employee_id;

【MySQL】多表查询_第7张图片

SELECT emp.employee_id AS "员工ID",emp.last_name AS "员工名字",mgr.manager_id AS "管理ID",mgr.last_name AS "管理名字" 
FROM employees emp,employees mgr
WHERE emp.manager_id = mgr.employee_id;

【MySQL】多表查询_第8张图片

#练习:查询出last_name为 ‘Chen’ 的员工的 manager 的信息。
SELECT emp.employee_id,emp.last_name AS "员工名字",mgr.last_name  as "管理员名字"
FROM employees emp,employees mgr 
WHERE emp.manager_id = mgr.employee_id
AND emp.last_name like '%Chen%';

运行

分类2:内连接 vs 外连接

概念:

  • 内连接: 合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行
  • 外连接: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。

内连接: 查出条件匹配的全部数据,那么有不匹配的数据,就不会显示出来了
举例:

SELECT employee_id,department_name
FROM employees e,departments d
WHERE e.`department_id` = d.department_id; -- 查出106条数据 --

可是 员工数据 有107条,也就是说 还有一个人没有对应的部门(也就是没有匹配到数据)


如果我们需要显示这个不匹配的数据,就是用外连接,让不匹配的显示为空(Null)

外连接分为:
- 左外连接
- 右外连接
- 满外连接

SQL92 实现外连接:

  • 在 SQL92 中采用(+)代表从表所在的位置。即左或右外连接中,(+) 表示哪个是从表。

  • Oracle 对 SQL92 支持较好,而 MySQL 则不支持 SQL92 的外连接。

    #左外连接
    SELECT last_name,department_name
    FROM employees ,departments
    WHERE employees.department_id = departments.department_id(+);
    
    #右外连接
    SELECT last_name,department_name
    FROM employees ,departments
    WHERE employees.department_id(+) = departments.department_id;
    
  • 而且在 SQL92 中,只有左外连接和右外连接,没有满(或全)外连接。

三、SQL99

以上代码都使用的SQL92
SQL99使用 JOIN …ON 的方式实现多表的查询

SQL99 实现 内连接

语法:

SELECT table1.column, table2.column,table3.column
FROM table1
    JOIN table2 ON table1 和 table2 的连接条件
        JOIN table3 ON table2 和 table3 的连接条件
# 查询员工ID与其对应的部门名字
SELECT e.department_id,d.department_name
FROM employees e JOIN departments d 
ON e.department_id = d.department_id;
# 查询员工名字,部门,城市
SELECT e.first_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;	

1

SQL99 实现 外连接(OUTER JOIN)

左外连接(LEFT OUTER JOIN)

语法:

#实现查询结果是A
SELECT 字段列表
FROM A表 LEFT JOIN B表
ON 关联条件
WHERE 等其他子句;

左外连接,以左边的表(A表)为主,完整的显示A表(包括不匹配的数据(为空数据也显示))

举例:

# 显示员工名字与对应部门
SELECT e.first_name,d.department_name
FROM employees e 
LEFT JOIN departments d 
ON e.department_id = d.department_id;

【MySQL】多表查询_第9张图片
员工有107条数据,包括为Null的,所以使用左外连接实现全显示

右外连接(RIGHT OUTER JOIN)

语法:

#实现查询结果是B
SELECT 字段列表
FROM A表 RIGHT JOIN B表
ON 关联条件
WHERE 等其他子句;

举例:

SELECT e.first_name,d.department_name
FROM employees e 
RIGHT JOIN departments d 
ON e.department_id = d.department_id;

【MySQL】多表查询_第10张图片
部门表中,还有很多部门没有员工,即为空

需要注意的是,LEFT JOIN 和 RIGHT JOIN 只存在于 SQL99 及以后的标准中,在 SQL92 中不存在,只能用 (+) 表示。

满外连接(FULL OUTER JOIN)

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

UNION 和 UNION ALL的使用

UNION:会执行去重操作
UNION ALL:不会执行去重操作
结论:如果明确知道合并数据后的结果数据不存在重复数据,或者不需要去除重复的数据,
则尽量使用UNION ALL语句,以提高数据查询的效率。

语法:

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

举例:

# 查询部门编号>90或邮箱包含a的员工信息
-- 方式一 -- 
SELECT *
FROM employees
WHERE department_id > 90 
OR email LIKE '%a%';
-- 方式二 --
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id > 90 ;

四、7种SQL JOINS的实现

【MySQL】多表查询_第11张图片

1. 内连接

1

# 举例:查询员工ID名字,部门名字
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e JOIN departments d ON e.department_id = d.department_id;

1

# 语法格式
SELECT 列名
FROM 表名A
JOIN 要连接的表B 
ON AB表关联字段;

2. 左外连接

2

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

在这里插入图片描述

#语法格式:
SELECT 列名
FROM 表名A
LEFT JOIN 表名B
ON AB关联字段;

3. 右外连接

3

# 右外连接
# 举例:查询员工ID名字,部门名字
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id;

【MySQL】多表查询_第12张图片

#语法格式:
SELECT 列名
FROM 表名A
RIGHT JOIN 表名B
ON AB关联字段;

4.左外连接 但查NULL(A - A∩B)

4
A 减去 A 交 B

# 左外连接但是查的是为Null的数值(也就是把不匹配的数据打印)
# 举例:查询员工ID名字,部门名字 为Null的
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id
WHERE d.department_id IS NULL;

在这里插入图片描述

#语法格式:
SELECT 列名
FROM 表名A
LEFT JOIN 表名B
ON AB关联字段
WHERE B.关联字段 IS NULL; -- 过滤条件:把B表为空的显示 --

5. 右外联接 但查Null(B - A∩B)

5

SELECT e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE  ISNULL(e.department_id);

5

#语法格式:
SELECT 列名
FROM 表名A
RIGHT JOIN 表名B
ON AB关联字段
WHERE A.关联字段 IS NULL;-- A表为空的显示 -- 

6.满外连接 A∪B

左外连接 + 右外连接
6

SELECT e.employee_id,e.last_name,d.department_name
FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id
UNION ALL -- 不去重 --
SELECT e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id;

【MySQL】多表查询_第13张图片

# 语法格式:
SELECT 列名 FROM A表 LEFT JOIN B表 ON AB关联字段
UNION (或是UNION ALL)
SELECT 列名 FROM A表 RIGHT JOIN B表 ON AB关联字段;

7. 满外连接 但是查的是Null -> (A - A∩B) ∪ (B - A∩B)

左 + 右 但是 只要不匹配数据 即为空数据
7

SELECT e.employee_id,e.last_name,d.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 e.employee_id,e.last_name,d.department_name
FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE e.employee_id IS NULL;

在这里插入图片描述

# 语法格式:
SELECT 列名 FROM A表 LEFT JOIN B表 ON AB关联字段
WHERE B表关联字段 IS NULL;
UNION (或是UNION ALL)
SELECT 列名 FROM A表 RIGHT JOIN B表 ON AB关联字段;
WHERE A表关联字段 IS NULL;

关联字段的表为空的全打印,不为空的过滤

五、SQL99 语法新特性

自然连接 NATURAL

NATURAL JOIN 用来表示自然连接。我们可以把自然连接理解为 SQL92 中的等值连接。它会帮你自动查询两张连接表中所有相同的字段,然后进行等值连接

#sql92 
# 查找员工ID,名字,部门,名字
# 关联两表 中的 部门ID 与 管理ID 
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`;

# sql99 自然连接
# 即把两表中所有的相同字段都关联了
SELECT employee_id,last_name,department_name
FROM employees e NATURAL JOIN departments d;

效果

USING连接

使用USING指定数据表里的同名字段进行等值连接。但是只能配合JOIN一起使用。
简化了代码:
1

# sql92 中也是 简化where代码
SELECT employee_id,last_name,department_name
FROM employees e ,departments d
WHERE e.department_id = d.department_id;

XG
JOIN...USING与自然连接 NATURAL JOIN 不同的是,USING 指定了具体的相同的字段名称,你需要在 USING 的括号 () 中填入要指定的同名字段

SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
USING (department_id);

总结

表连接的约束条件可以有三种方式:WHERE, ON, USING

  • WHERE:适用于所有关联查询
  • ON:只能和JOIN一起使用,只能写关联条件。虽然关联条件可以并到WHERE中和其他条件一起写,但分开写可读性更好。
  • USING:只能和JOIN一起使用,而且要求两个关联字段在关联表中名称一致,而且只能表示关联字段值相等
#关联条件
#把关联条件写在where后面
SELECT last_name,department_name 
FROM employees,departments 
WHERE employees.department_id = departments.department_id;

#把关联条件写在on后面,只能和JOIN一起使用
SELECT last_name,department_name 
FROM employees INNER JOIN departments 
ON employees.department_id = departments.department_id;

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

SELECT last_name,department_name  
FROM employees JOIN departments 
ON employees.department_id = departments.department_id;

#把关联字段写在using()中,只能和JOIN一起使用
#而且两个表中的关联字段必须名称相同,而且只能表示=
#查询员工姓名与基本工资
SELECT last_name,job_title
FROM employees INNER JOIN jobs USING(job_id);

#n张表关联,需要n-1个关联条件
#查询员工姓名,基本工资,部门名称
SELECT last_name,job_title,department_name FROM employees,departments,jobs 
WHERE employees.department_id = departments.department_id 
AND employees.job_id = jobs.job_id;

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;

注意:
我们要控制连接表的数量。多表连接就相当于嵌套 for 循环一样,非常消耗资源,会让 SQL 查询性能下降得很严重,因此不要连接不必要的表。在许多 DBMS 中,也都会有最大连接表的限制。

【强制】超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时, 保证被关联的字段需要有索引。
说明:即使双表 join 也要注意表索引、SQL 性能。
来源:阿里巴巴《Java开发手册》

综合练习

你可能感兴趣的:(MySQL,mysql,数据库,sql)