MySQL 基础篇(八):复合查询之多表查询、内外连接、子查询

目录

本文内容概要

一、测试数据库设计

[1.1 创建数据库及数据表](#1.1 创建数据库及数据表)

[1.2 插入测试数据](#1.2 插入测试数据)

二、多表查询

[2.1 什么是多表查询](#2.1 什么是多表查询)

[2.2 多表之间的关系](#2.2 多表之间的关系)

[2.3 笛卡尔积问题](#2.3 笛卡尔积问题)

[2.4 多表查询的样例](#2.4 多表查询的样例)

三、内连接查询

四、外连接查询

[4.1 什么是外连接](#4.1 什么是外连接)

[4.2 左外连接](#4.2 左外连接)

[4.3 右外连接](#4.3 右外连接)

[4.4 ON 与 WHERE 的区别](#4.4 ON 与 WHERE 的区别)

五、自连接查询

六、联合查询

[6.1 什么是联合查询](#6.1 什么是联合查询)

[6.2 UNION](#6.2 UNION)

[6.3 UNION ALL](#6.3 UNION ALL)

[6.4 联合查询的注意事项](#6.4 联合查询的注意事项)

七、子查询

[7.1 什么是子查询](#7.1 什么是子查询)

[7.2 标量子查询](#7.2 标量子查询)

[7.3 多列子查询](#7.3 多列子查询)

[7.4 多行子查询](#7.4 多行子查询)

[7.5 表子查询](#7.5 表子查询)


本文内容概要

本文主要介绍 MySQL 中复合查询的相关知识。通过本文的学习,需要理解多表之间的关联关系 及笛卡尔积 的产生原因;掌握内连接、外连接和自连接 的基本原理、语法及应用场景;理解联合查询的作用 ,掌握 UNION 和 UNION ALL的使用方法及区别;掌握子查询的基本概念,以及标量子查询、多列子查询、多行子查询和表子查询的使用方法。通过结合实际案例,进一步提高编写和理解复杂 SQL 查询语句的能力。

一、测试数据库设计

在正式开始本篇文章的学习之前,我们需要先准备一些测试数据,用于后续多表查询、联合查询以及子查询等操作的演示与验证。

本文将统一使用以下测试数据进行讲解,以便更直观地理解不同查询方式的执行结果。当然,你也可以使用自己已有的测试数据进行练习,无须与本文的数据完全一致。

本文准备创建一个名为 mysql_blog 的数据库,包含三张表:

dept(部门表)

dept_id · dept_name · location

emp(员工表)

emp_id · emp_name · gender · dept_id · manager_id · salary · hire_date

salary_grade(工资等级表)

grade_id · grade_name · min_salary · max_salary

1.1 创建数据库及数据表

下面的 SQL 可以直接在 MySQL 客户端执行。

注:脚本会删除 mysql_blog 数据库同名的三张表,如果已有数据,请先备份!

sql 复制代码
CREATE DATABASE IF NOT EXISTS mysql_blog
DEFAULT CHARACTER SET utf8mb4;

USE mysql_blog;

DROP TABLE IF EXISTS emp;
DROP TABLE IF EXISTS salary_grade;
DROP TABLE IF EXISTS dept;

-- 1. 部门表
CREATE TABLE dept (
    dept_id INT PRIMARY KEY,
    dept_name VARCHAR(30) NOT NULL,
    location VARCHAR(30)
);

-- 2. 员工表
CREATE TABLE emp (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(30) NOT NULL,
    gender CHAR(1),
    dept_id INT,
    manager_id INT,
    salary DECIMAL(10, 2),
    hire_date DATE,
    FOREIGN KEY (dept_id) REFERENCES dept(dept_id),
    FOREIGN KEY (manager_id) REFERENCES emp(emp_id)
);

-- 3. 工资等级表
CREATE TABLE salary_grade (
    grade_id INT PRIMARY KEY,
    grade_name VARCHAR(20),
    min_salary DECIMAL(10, 2),
    max_salary DECIMAL(10, 2)
);

1.2 插入测试数据

部门表:

sql 复制代码
INSERT INTO dept VALUES
(10, '研发部', '北京'),
(20, '产品部', '北京'),
(30, '市场部', '上海'),
(40, '人事部', '北京'),
(50, '财务部', '深圳'),
(60, '法务部', '广州');

员工表:员工表设计为 16 名员工,包含总经理、部门负责人和普通员工三个等级

sql 复制代码
-- 总经理
INSERT INTO emp VALUES
(1001, '李明', '男', NULL, NULL, 30000, '2020-01-10');

-- 部门负责人
INSERT INTO emp VALUES
(1002, '张伟', '男', 10, 1001, 22000, '2021-03-15'),
(1006, '赵强', '男', 20, 1001, 21000, '2021-05-20'),
(1009, '吴敏', '女', 30, 1001, 18000, '2021-07-11'),
(1012, '何林', '男', 40, 1001, 13000, '2022-02-08'),
(1014, '钱涛', '男', 50, 1001, 19000, '2021-09-16');

-- 普通员工
INSERT INTO emp VALUES
(1003, '王芳', '女', 10, 1002, 16000, '2022-06-01'),
(1004, '刘洋', '男', 10, 1002, 15000, '2023-01-12'),
(1005, '陈静', '女', 10, 1002, 16000, '2023-04-18'),
(1016, '马悦', '女', 10, 1002, 15000, '2024-05-06'),
(1007, '孙丽', '女', 20, 1006, 14000, '2022-08-22'),
(1008, '周杰', '男', 20, 1006, 12000, '2023-03-09'),
(1010, '郑凯', '男', 30, 1009, 11000, '2022-11-14'),
(1011, '冯雪', '女', 30, 1009, 11000, '2023-07-20'),
(1013, '高楠', '女', 40, 1012, 9000, '2024-01-05'),
(1015, '白露', '女', NULL, 1001, 8000, '2024-03-01');

工资等级表:

sql 复制代码
INSERT INTO salary_grade VALUES
(1, '一级', 0, 9999),
(2, '二级', 10000, 14999),
(3, '三级', 15000, 19999),
(4, '四级', 20000, 29999),
(5, '五级', 30000, 999999);

二、多表查询

2.1 什么是多表查询

在前面的学习中,我们已经掌握了单表查询的基本操作,例如条件查询、排序查询、分组查询以及聚合查询等。但是,在实际开发中,数据库中的数据通常不会全部存储在同一张表中,而是根据不同的业务需求,将数据拆分到多张表中进行管理。

例如,在员工管理系统中,我们可以分别创建员工表(emp)和部门表(empt)。

员工表(emp)

emp_id emp_name dept_id salary
1002 张伟 10 22000
1003 王芳 10 16000
1006 赵强 20 21000
1015 白露 NULL 8000

部门表(dept)

dept_id dept_name location
10 研发部 北京
20 产品部 北京
30 市场部 上海
60 法务部 广州

通过观察可以发现,员工表中仅记录了员工所属部门的编号,而部门的具体名称、所在位置等信息存储在部门表中。

假设现在需要查询所有员工的姓名及其所属部门名称。显然,如果只查询员工表,我们只能获取部门编号,无法直接得到部门名称。此时,就需要根据两张表之间的关联关系,将员工信息与部门信息组合起来进行查询。

这种同时涉及多张数据表,并通过一定的关联条件获取所需数据的查询方式,称为多表查询。

2.2 多表之间的关系

在进行多表查询之前,我们首先需要理解不同数据表之间的关联关系。

通常情况下,数据表之间的关系可以分为以下三种:

(1)一对一关系

一张表中的一条记录,最多对应另一张表中的一条记录。

例如,用户基本信息表与用户详细信息表之间,就可以建立一对一关系。

在实际设计中,通常通过主键或唯一约束配合外键来维护这种关系。

(2)一对多关系

一张表中的一条记录,可以对应另一张表中的多条记录;而另一张表中的一条记录,最多对应前一张表中的一条记录。

例如,本文使用的部门表与员工表之间,就是典型的一对多关系。

一个部门可以拥有多名员工,而在当前数据库设计中,一名员工最多属于一个部门。

员工表中的 dept_id 字段用于关联部门表中的 dept_id 字段。

(3)多对多关系

一张表中的一条记录,可以对应另一张表中的多条记录。

例如,学生与课程之间就存在多对多关系。

一名学生可以选修多门课程,而一门课程也可以被多名学生选修。

这种关系通常需要引入第三张表,也就是中间关联表,记录学生与课程之间的对应关系。

需要注意的是,**数据表之间的关联关系,本质上由业务逻辑决定,而外键约束则可以用于维护这些关联数据的完整性。**即使没有显式创建外键约束,只要存在合理的关联字段,我们仍然可以通过 SQL 实现多表查询。

2.3 笛卡尔积问题

既然有了多表查询的需求,在 MySQL 中该如何进行多表查询呢:

sql 复制代码
select * from emp, dept;
+--------+----------+--------+---------+------------+----------+------------+---------+-----------+----------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  | dept_id | dept_name | location |
+--------+----------+--------+---------+------------+----------+------------+---------+-----------+----------+
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |      10 | 研发部    | 北京     |
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |      20 | 产品部    | 北京     |
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |      30 | 市场部    | 上海     |
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |      40 | 人事部    | 北京     |
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |      50 | 财务部    | 深圳     |
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |      60 | 法务部    | 广州     |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |      10 | 研发部    | 北京     |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |      20 | 产品部    | 北京     |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |      30 | 市场部    | 上海     |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |      40 | 人事部    | 北京     |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |      50 | 财务部    | 深圳     |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |      60 | 法务部    | 广州     |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |      10 | 研发部    | 北京     |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |      20 | 产品部    | 北京     |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |      30 | 市场部    | 上海     |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |      40 | 人事部    | 北京     |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |      50 | 财务部    | 深圳     |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |      60 | 法务部    | 广州     |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |      10 | 研发部    | 北京     |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |      20 | 产品部    | 北京     |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |      30 | 市场部    | 上海     |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |      40 | 人事部    | 北京     |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |      50 | 财务部    | 深圳     |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |      60 | 法务部    | 广州     |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |      10 | 研发部    | 北京     |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |      20 | 产品部    | 北京     |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |      30 | 市场部    | 上海     |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |      40 | 人事部    | 北京     |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |      50 | 财务部    | 深圳     |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |      60 | 法务部    | 广州     |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |      10 | 研发部    | 北京     |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |      20 | 产品部    | 北京     |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |      30 | 市场部    | 上海     |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |      40 | 人事部    | 北京     |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |      50 | 财务部    | 深圳     |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |      60 | 法务部    | 广州     |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |      10 | 研发部    | 北京     |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |      20 | 产品部    | 北京     |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |      30 | 市场部    | 上海     |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |      40 | 人事部    | 北京     |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |      50 | 财务部    | 深圳     |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |      60 | 法务部    | 广州     |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |      10 | 研发部    | 北京     |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |      20 | 产品部    | 北京     |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |      30 | 市场部    | 上海     |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |      40 | 人事部    | 北京     |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |      50 | 财务部    | 深圳     |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |      60 | 法务部    | 广州     |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |      10 | 研发部    | 北京     |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |      20 | 产品部    | 北京     |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |      30 | 市场部    | 上海     |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |      40 | 人事部    | 北京     |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |      50 | 财务部    | 深圳     |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |      60 | 法务部    | 广州     |
|   1010 | 郑凯     | 男     |      30 |       1009 | 11000.00 | 2022-11-14 |      10 | 研发部    | 北京     |
|   1010 | 郑凯     | 男     |      30 |       1009 | 11000.00 | 2022-11-14 |      20 | 产品部    | 北京     |
|   1010 | 郑凯     | 男     |      30 |       1009 | 11000.00 | 2022-11-14 |      30 | 市场部    | 上海     |
|   1010 | 郑凯     | 男     |      30 |       1009 | 11000.00 | 2022-11-14 |      40 | 人事部    | 北京     |
|   1010 | 郑凯     | 男     |      30 |       1009 | 11000.00 | 2022-11-14 |      50 | 财务部    | 深圳     |
|   1010 | 郑凯     | 男     |      30 |       1009 | 11000.00 | 2022-11-14 |      60 | 法务部    | 广州     |
|   1011 | 冯雪     | 女     |      30 |       1009 | 11000.00 | 2023-07-20 |      10 | 研发部    | 北京     |
|   1011 | 冯雪     | 女     |      30 |       1009 | 11000.00 | 2023-07-20 |      20 | 产品部    | 北京     |
|   1011 | 冯雪     | 女     |      30 |       1009 | 11000.00 | 2023-07-20 |      30 | 市场部    | 上海     |
|   1011 | 冯雪     | 女     |      30 |       1009 | 11000.00 | 2023-07-20 |      40 | 人事部    | 北京     |
|   1011 | 冯雪     | 女     |      30 |       1009 | 11000.00 | 2023-07-20 |      50 | 财务部    | 深圳     |
|   1011 | 冯雪     | 女     |      30 |       1009 | 11000.00 | 2023-07-20 |      60 | 法务部    | 广州     |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |      10 | 研发部    | 北京     |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |      20 | 产品部    | 北京     |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |      30 | 市场部    | 上海     |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |      40 | 人事部    | 北京     |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |      50 | 财务部    | 深圳     |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |      60 | 法务部    | 广州     |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |      10 | 研发部    | 北京     |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |      20 | 产品部    | 北京     |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |      30 | 市场部    | 上海     |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |      40 | 人事部    | 北京     |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |      50 | 财务部    | 深圳     |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |      60 | 法务部    | 广州     |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |      10 | 研发部    | 北京     |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |      20 | 产品部    | 北京     |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |      30 | 市场部    | 上海     |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |      40 | 人事部    | 北京     |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |      50 | 财务部    | 深圳     |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |      60 | 法务部    | 广州     |
|   1015 | 白露     | 女     |    NULL |       1001 |  8000.00 | 2024-03-01 |      10 | 研发部    | 北京     |
|   1015 | 白露     | 女     |    NULL |       1001 |  8000.00 | 2024-03-01 |      20 | 产品部    | 北京     |
|   1015 | 白露     | 女     |    NULL |       1001 |  8000.00 | 2024-03-01 |      30 | 市场部    | 上海     |
|   1015 | 白露     | 女     |    NULL |       1001 |  8000.00 | 2024-03-01 |      40 | 人事部    | 北京     |
|   1015 | 白露     | 女     |    NULL |       1001 |  8000.00 | 2024-03-01 |      50 | 财务部    | 深圳     |
|   1015 | 白露     | 女     |    NULL |       1001 |  8000.00 | 2024-03-01 |      60 | 法务部    | 广州     |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |      10 | 研发部    | 北京     |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |      20 | 产品部    | 北京     |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |      30 | 市场部    | 上海     |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |      40 | 人事部    | 北京     |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |      50 | 财务部    | 深圳     |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |      60 | 法务部    | 广州     |
+--------+----------+--------+---------+------------+----------+------------+---------+-----------+----------+
96 rows in set (0.00 sec)

上述 SQL 同时查询员工表与部门表,从逻辑上看,员工表中的每一条记录,都会与部门表中的每一条记录进行组合,形成一张新的 "表"。

这种组合方式成为笛卡尔积。

假设员工表中存在 m 条记录,部门表中存在 n 条记录,那么两张表产生的笛卡尔积就是 m * n 条记录。

对于本文准备的测试数据而言:员工表 16 条记录,部门表 6 条记录。两张表的笛卡尔积就是 96 条记录。

但是在实际查询中,这 96 条记录并不都是我们真正想要的数据。

例如,研发部员工会与其他部门的信息组合在一起,从而产生不符合实际业务关系。

那么,如何消除这些不符合业务关系的组合呢?

其实很简单。在前面的学习中,我们已经知道,WHERE 子句可以根据指定条件对数据进行筛选。

对于多表查询而言,我们可以从逻辑上将 FROM 子句产生的数据组合看作一张临时的结果表,然后利用 WHERE 子句对其进行条件筛选,只保留符合业务关系的记录。

sql 复制代码
select * from emp, dept where emp.dept_id = dept.dept_id;
+--------+----------+--------+---------+------------+----------+------------+---------+-----------+----------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  | dept_id | dept_name | location |
+--------+----------+--------+---------+------------+----------+------------+---------+-----------+----------+
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |      10 | 研发部    | 北京     |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |      10 | 研发部    | 北京     |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |      10 | 研发部    | 北京     |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |      10 | 研发部    | 北京     |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |      10 | 研发部    | 北京     |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |      20 | 产品部    | 北京     |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |      20 | 产品部    | 北京     |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |      20 | 产品部    | 北京     |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |      30 | 市场部    | 上海     |
|   1010 | 郑凯     | 男     |      30 |       1009 | 11000.00 | 2022-11-14 |      30 | 市场部    | 上海     |
|   1011 | 冯雪     | 女     |      30 |       1009 | 11000.00 | 2023-07-20 |      30 | 市场部    | 上海     |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |      40 | 人事部    | 北京     |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |      40 | 人事部    | 北京     |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |      50 | 财务部    | 深圳     |
+--------+----------+--------+---------+------------+----------+------------+---------+-----------+----------+
14 rows in set (0.01 sec)

对于本文的测试数据,最终可以得到 14 条记录。需要注意,员工表中还有两名员工的 dept_id 为 NULL,因此这两名员工不会出现在上述查询结果中。

多表查询的关键:不仅在于同时查询多张表,更在于根据合理的条件,获取符合业务需求的数据。

最后需要补充一点:笛卡尔积主要用于帮助我们从逻辑上理解多表查询的组合过程,并不意味着 MySQL 在执行所有多表查询时,都必须先实际生成完整的笛卡尔积。数据库优化器可以根据查询条件选择更高效的执行方式。

2.4 多表查询的样例

案例一:查询所有员工的姓名、工资、以及所属部门名称

sql 复制代码
select emp_name, salary,dept_name from emp, dept where emp.dept_id = dept.dept_id;

案例二:查询北京地区工作薪资大于 15000 的员工姓名、部门、薪资,并按照工资降序排列

sql 复制代码
select emp_name, dept_name, salary from emp, dept where emp.dept_id = dept.dept_id and location = '.京 ' andsalary > 15000 order by salary desc;

案例三:统计各部门的员工人数,显示部门名和人数,并按照人数降序排列

sql 复制代码
select dept_name,count(*) emp_count from emp, dept where emp.dept_id = dept.dept_id group by dept.dept_id order by emp_count desc;

需要注意的是,由于法务部没有员工,因此无法通过上述关联条件匹配到任何记录,自然不会出现在统计结果中。那么,如果我们希望查询结果能够保留所有部门,应该如何实现呢?

这就需要使用后面要介绍的外连接查询。

通过以上几个案例,可以发现,多表查询与基本查询并不冲突,我们只需要根据不同数据表之间的关联关系,确定合理的查询条件,就可以在多表数据的基础上继续进行筛选、排序以及分组统计等操作。

三、内连接查询

内连接(INNER JOIN) 是多表查询中最常见的一种连接方式,其核心思想是根据指定的关联条件,对多张表中的数据进行匹配,只保留满足关联条件的数据组合。

从逻辑上理解,我们可以将内连接看作对多张表形成的笛卡尔积进行条件筛选的过程。

实际上,在前面 2.4 小节中,我们已经通过 WHERE 子句对多张表形成的笛卡尔积进行了筛选,这就是一种典型的内连接查询。

在 MySQL 中,根据 SQL 语句的书写方式,可以将内连接分为两种:

  • 隐式内连接:通过 FROM 子句列出多张表,再使用 WHERE 子句指定关联条件。

  • 显式内连接:通过 INNER JOIN ... ON 语法明确指定需要连接的表及其关联条件。

虽然两种方式在语法上有所不同,但在连接条件相同的情况下,其查询结果是一致的。

案例一:查询所有员工的姓名、工资以及所属部门

sql 复制代码
--隐式内连接
select emp_name, salary,dept_name from emp,dept where dept.dept_id = emp.dept_id;

--显示内连接
select emp_name, salary,dept_name from emp inner join dept on dept.dept_id = emp.dept_id;
--inner 可以省略不写
select emp_name, salary,dept_name from emp join dept on dept.dept_id = emp.dept_id;

案例二:查询所有员工的姓名、所属部门、工资及其对应的工资等级

sql 复制代码
select emp_name, dept_id, salary, grade_name from emp join salary_gradeon salary >= min_salary and salary <= max_salary;

四、外连接查询

4.1 什么是外连接

在前面的学习中,内连接的条件筛选只会保留满足关联条件的数据组合。

例如,我们希望查询所有员工的姓名及其所属部门,可以使用:

sql 复制代码
select emp_name, dept_name from emp inner join dept on emp.dept_id = dept.dept_id;

select count(*) from emp inner join dept on emp.dept_id = dept.dept_id;
+----------+
| count(*) |
+----------+
|       14 |
+----------+

select count(*) from emp;
+----------+
| count(*) |
+----------+
|       16 |
+----------+
1 row in set (0.00 sec)

但是,这里存在一个问题:在我们的测试数据中,员工表有 16 条记录,其中 2 名员工没有所属部分,而部门表中还有一个没有任何员工的法务部。

由于这些记录无法满足内连接的关联条件,因此不会出现在查询结果中。那么,如果我们希望保留某张表中的所有记录,即使其中某些记录无法与另一张表匹配,该如何处理。

此时,就需要使用外连接。外连接(OUTER JOIN)是多表查询中的一种连接方式。与内连接不同,外连接不仅会返回满足关联条件的数据组合,还会保留指定一侧数据表中未匹配成功的记录。

对于这些未匹配成功的记录,MySQL 会使用 NULL 值填充另一侧数据表对应的字段。

在 MySQL 中,常见的外连接有两种:

  • 左外连接(LEFT JOIN):保留左表的全部记录
  • 右外连接(RIGHT JOIN):保留右表的全部记录

4.2 左外连接

案例一:查询所有员工的姓名及其所属部门名称,包括没有部门的员工

sql 复制代码
select emp_name, dept_name from emp left join dept on emp.dept_id = dept.dep.dept_id;
+----------+-----------+
| emp_name | dept_name |
+----------+-----------+
| 李明     | NULL      |
| 张伟     | 研发部    |
| 王芳     | 研发部    |
| 刘洋     | 研发部    |
| 陈静     | 研发部    |
| 赵强     | 产品部    |
| 孙丽     | 产品部    |
| 周杰     | 产品部    |
| 吴敏     | 市场部    |
| 郑凯     | 市场部    |
| 冯雪     | 市场部    |
| 何林     | 人事部    |
| 高楠     | 人事部    |
| 钱涛     | 财务部    |
| 白露     | NULL      |
| 马悦     | 研发部    |
+----------+-----------+
16 rows in set (0.01 sec)

案例二:查询所有部门员工,包括没有员工的部门

sql 复制代码
select dept_name, emp_name from dept left join emp on dept.dept_id = emp.dept_id;
+-----------+----------+
| dept_name | emp_name |
+-----------+----------+
| 研发部    | 张伟     |
| 研发部    | 王芳     |
| 研发部    | 刘洋     |
| 研发部    | 陈静     |
| 研发部    | 马悦     |
| 产品部    | 赵强     |
| 产品部    | 孙丽     |
| 产品部    | 周杰     |
| 市场部    | 吴敏     |
| 市场部    | 郑凯     |
| 市场部    | 冯雪     |
| 人事部    | 何林     |
| 人事部    | 高楠     |
| 财务部    | 钱涛     |
| 法务部    | NULL     |
+-----------+----------+
15 rows in set (0.00 sec)

4.3 右外连接

右外连接与左外连接类似,只不过保留记录的方向发生了变化

案例一:查询所有部门及其员工信息,包括没有员工的部门

sql 复制代码
select dept_name, emp_name from emp right join dept on dept.dept_id = emp.dept_id;
+-----------+----------+
| dept_name | emp_name |
+-----------+----------+
| 研发部    | 张伟     |
| 研发部    | 王芳     |
| 研发部    | 刘洋     |
| 研发部    | 陈静     |
| 研发部    | 马悦     |
| 产品部    | 赵强     |
| 产品部    | 孙丽     |
| 产品部    | 周杰     |
| 市场部    | 吴敏     |
| 市场部    | 郑凯     |
| 市场部    | 冯雪     |
| 人事部    | 何林     |
| 人事部    | 高楠     |
| 财务部    | 钱涛     |
| 法务部    | NULL     |
+-----------+----------+
15 rows in set (0.00 sec)

此时,由于 dept 位于 RIGHT JOIN 的右侧,因此部门表的所有记录被保留。

可以发现,只需要交换两张表的位置,并将 RIGHT JOIN 改为 LEFT JOIN,就能得到相同的查询结果。因此,在实际开发中,我们完全可以根据需求来调整表的位置,统一采用 LEFT JOIN 来实现外连接查询。

4.4 ON 与 WHERE 的区别

在内连接中,ON 与 WHER 的条件筛选是等价的。但是,在外连接中,将某个条件放在 ON 或者 WHERE 中,可能直接影响最终的查询结果。下面通过一个案例进行说明:

案例:查询所有部门工资大于 2000 的员工,没有员工满足的部门也要显示。

sql 复制代码
-- 在 ON 中添加筛选条件
select dept_name, emp_name, salary from dept left join emp on dept.dept_id = emp.dept_id and salary > 20000;
+-----------+----------+----------+
| dept_name | emp_name | salary   |
+-----------+----------+----------+
| 研发部    | 张伟     | 22000.00 |
| 产品部    | 赵强     | 21000.00 |
| 市场部    | NULL     |     NULL |
| 人事部    | NULL     |     NULL |
| 财务部    | NULL     |     NULL |
| 法务部    | NULL     |     NULL |
+-----------+----------+----------+
6 rows in set (0.00 sec)
 
-- 在 WHERE 中添加筛选条件
select dept_name, emp_name, salary from dept left join emp on dept.dept_id = emp.dept_id where salary > 20000;
+-----------+----------+----------+
| dept_name | emp_name | salary   |
+-----------+----------+----------+
| 研发部    | 张伟     | 22000.00 |
| 产品部    | 赵强     | 21000.00 |
+-----------+----------+----------+
2 rows in set (0.00 sec)

通过案例,可以发现,WHERE 筛选出来的查询结果中,其他部门消失了。这是因为我们可以从逻辑上将 WHERE 理解为在连接结果产生之后进行筛选。对于没有匹配的员工的部门,其 salary 为 NULL,当进行 WHERE 条件判断时,不为真,会被过滤。

五、自连接查询

自连接是指一张数据表与自身进行连接查询的方式。

从逻辑上理解,我们可以将同一张表看作两张独立的逻辑表,对它们进行连接,并根据指定的关联条件筛选出符合要求的数据组合。需要注意的是,由于参与连接的两张逻辑表实际上来源于同一张数据表,因此我们通常需要为它们设置不同的表别名,以区分不同的数据来源。此外,自连接并不是一种独立的连接类型,它既可以使用内连接,也可以使用外连接,具体取决于实际的查询需求。

案例:查询每个员工的姓名及其直属领导的姓名

sql 复制代码
select t1.emp_name 员工, t2.emp_name 直属领导 from emp t1, emp t2 where t1.manager_id = t2.emp_id;
+--------+--------------+
| 员工   | 直属领导     |
+--------+--------------+
| 张伟   | 李明         |
| 王芳   | 张伟         |
| 刘洋   | 张伟         |
| 陈静   | 张伟         |
| 赵强   | 李明         |
| 孙丽   | 赵强         |
| 周杰   | 赵强         |
| 吴敏   | 李明         |
| 郑凯   | 吴敏         |
| 冯雪   | 吴敏         |
| 何林   | 李明         |
| 高楠   | 何林         |
| 钱涛   | 李明         |
| 白露   | 李明         |
| 马悦   | 张伟         |
+--------+--------------+
15 rows in set (0.00 sec)

由于李明没有直属领导,因此在内连接中无法匹配成功,如果希望保留李明,可以使用外连接。

六、联合查询

6.1 什么是联合查询

在实际应用中,可能存在合并多个 SELECT 的执行结果,此时可以使用集合操作符 UNION、UNION ALL 来完成。

联合查询是指通过 UNION 或者 UNION ALL,将多个 SELECT 语句的查询结果合并成一个结果进行展示。

在 MySQL 中,联合查询主要有两种:

  • UNION:合并多个查询结果,并自动去除重复记录
  • UNION ALL:合并多个查询结果,保留所有记录,不会去重

基本语法:

sql 复制代码
SELECT 字段列表 FROM 表名 WHERE 条件
UNION [ALL]
SELECT 字段列表 FROM 表名 WHERE 条件;

需要注意的是:联合查询可以合并来自同一张表的多个查询结果,也可以合并来自不同数据表的查询结果。

6.2 UNION

案例:查询研发部的员工和工资大于等于 16000 的员工

sql 复制代码
-- 一条 SQL 语句
select emp_id, emp_name, salary from emp where dept_id = 10 or salary >= 16000;

-- 查询研发部的员工
select emp_id, emp_name, salary from emp where dept_id = 10;
+--------+----------+----------+
| emp_id | emp_name | salary   |
+--------+----------+----------+
|   1002 | 张伟     | 22000.00 |
|   1003 | 王芳     | 16000.00 |
|   1004 | 刘洋     | 15000.00 |
|   1005 | 陈静     | 16000.00 |
|   1016 | 马悦     | 15000.00 |
+--------+----------+----------+
5 rows in set (0.00 sec)

-- 查询工资大于等于 16000 的员工
select emp_id, emp_name, salary from emp where salary >= 16000;
+--------+----------+----------+
| emp_id | emp_name | salary   |
+--------+----------+----------+
|   1001 | 李明     | 30000.00 |
|   1002 | 张伟     | 22000.00 |
|   1003 | 王芳     | 16000.00 |
|   1005 | 陈静     | 16000.00 |
|   1006 | 赵强     | 21000.00 |
|   1009 | 吴敏     | 18000.00 |
|   1014 | 钱涛     | 19000.00 |
+--------+----------+----------+
7 rows in set (0.00 sec)

-- 合并两次查询结果并组织到一起
select emp_id, emp_name, salary from emp where salary >= 16000 union select emp_id, emp_name, salary from emp where dept_id = 10;
+--------+----------+----------+
| emp_id | emp_name | salary   |
+--------+----------+----------+
|   1001 | 李明     | 30000.00 |
|   1002 | 张伟     | 22000.00 |
|   1003 | 王芳     | 16000.00 |
|   1005 | 陈静     | 16000.00 |
|   1006 | 赵强     | 21000.00 |
|   1009 | 吴敏     | 18000.00 |
|   1014 | 钱涛     | 19000.00 |
|   1004 | 刘洋     | 15000.00 |
|   1016 | 马悦     | 15000.00 |
+--------+----------+----------+
9 rows in set (0.00 sec)

可以发现,虽然两次查询分别返回 5 条和 7 条记录,但最终只有 9 条记录。这是因为张伟、王芳和陈静同时满足两个查询条件,因此在合并时产生了重复记录。而 UNION 会自动去除这些重复记录,使它们在最终结果集中只出现一次。

需要注意,UNION **判断重复的依据是查询结果中的整行数据,而不是某一个特定字段。**只有当两条结果记录中所有对应字段的值都相同时,才会被视为重复记录。

6.3 UNION ALL

我们仍然使用前面的案例,只需要将 UNION 修改为 UNION ALL。

sql 复制代码
select emp_id, emp_name, salary from emp where salary >= 16000 union all select emp_id, emp_name, salary from emp where dept_id = 10;
+--------+----------+----------+
| emp_id | emp_name | salary   |
+--------+----------+----------+
|   1001 | 李明     | 30000.00 |
|   1002 | 张伟     | 22000.00 |
|   1003 | 王芳     | 16000.00 |
|   1005 | 陈静     | 16000.00 |
|   1006 | 赵强     | 21000.00 |
|   1009 | 吴敏     | 18000.00 |
|   1014 | 钱涛     | 19000.00 |
|   1002 | 张伟     | 22000.00 |
|   1003 | 王芳     | 16000.00 |
|   1004 | 刘洋     | 15000.00 |
|   1005 | 陈静     | 16000.00 |
|   1016 | 马悦     | 15000.00 |
+--------+----------+----------+
12 rows in set (0.00 sec)

可以发现,这一次得到了12条记录,表中也出现的重复的记录。

6.4 联合查询的注意事项

  1. 多个 SELECT 语句返回的字段数量必须相同
  2. 对应位置的字段应具有兼容的数据类型
  3. 联合查询结果的字段名称由第一个 SELECT 决定,起别名只允许第一个 SELECT 决定
  4. ORDER BY 和 LIMIT 可以用于整个联合查询结果

既然能够使用 OR 组合多个筛选条件,为什么还需要使用 UNION ?

在我们演示的案例中,它可以通过 OR 来完成查询目的。

但是,二者并不完全等价:

  • OR 是在同一次查询中组合多个筛选条件
  • UNION 是合并多个独立的查询结果,可能涉及不同数据表、不同的查询结构

而且,UNION ALL 会保留重复记录,显然不能简单地用 OR 替代。

虽然联合查询具有其独特的作用,但是在常规的业务数据查询中,联合查询很少使用,通常使用连接查询和条件查询。

七、子查询

7.1 什么是子查询

子查询是指嵌入在其他 SQL 语句的 SELECT 查询,也叫做嵌套查询。其中,包含子查询的 SQL 语句通常被称为外层查询,而嵌套在其中的 SELECT 查询被称为内层查询。

通过子查询,我们可以将一个查询的结果作为另一个查询的查询条件或数据来源,从而实现更加复杂的数据查询。

子查询类型 返回结果
标量子查询 单行单列
多列子查询 单行多列
多行子查询 多行单列
表子查询 多行多列

7.2 标量子查询

标量子查询的查询结果是单行单列的。

案例:查询所有大于平均工资的员工信息

sql 复制代码
--不采用子查询的方式
--1.先计算平均工资 2.使用平均工资进行筛选
select avg(salary) from emp;
+--------------+
| avg(salary)  |
+--------------+
| 15625.000000 |
+--------------+
select * from emp where salary > 15625;
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |
+--------+----------+--------+---------+------------+----------+------------+
--采用子查询方式,将第一步的查询结果作为第二步的查询条件
select * from emp where salary > (select avg(salary) from emp);
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |
|   1009 | 吴敏     | 女     |      30 |       1001 | 18000.00 | 2021-07-11 |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |
+--------+----------+--------+---------+------------+----------+------------+

7.3 多列子查询

列子查询的查询结果是单行多列的结果。

案例:查询与王芳属于同一部门,并且工资相同的员工

sql 复制代码
--不采用子查询的方式
--1.查询王芳的部门和工资 2.将查询的结果当作条件筛选
select dept_id, salary from emp where emp_name = '王芳';
+---------+----------+
| dept_id | salary   |
+---------+----------+
|      10 | 16000.00 |
+---------+----------+
select * from emp where dept_id = 10 and salary = 16000;
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |
+--------+----------+--------+---------+------------+----------+------------+
--采用子查询的方式
select * from emp where (dept_id, salary) = (select dept_id, salary from emp where emp_name = '王芳');
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |
+--------+----------+--------+---------+------------+----------+------------+

WHERE (dept_id, salary) = (...) : MySQL 支持通过括号将列属性组合起来,进行逐列比较。只有当每一列都满足条件时,该记录才会被保留。

7.4 多行子查询

多行子查询的查询结果是多行单列的结果。

案例一:查询与张伟部门相同或者与何林部门相同的所有员工

sql 复制代码
--不采用子查询
--1.查询张伟和何林的部门 2.根据已有部门进行条件筛选
select dept_id from emp where emp_name = '张伟' or emp_name = '何林';
+---------+
| dept_id |
+---------+
|      10 |
|      40 |
+---------+
select * from emp where dept_id = 10 or dept_id = 40;
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |
+--------+----------+--------+---------+------------+----------+------------+

select * from emp where dept_id in (select dept_id from emp where emp_name
= '张伟' or emp_name = '何林');
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |
|   1013 | 高楠     | 女     |      40 |       1012 |  9000.00 | 2024-01-05 |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |
+--------+----------+--------+---------+------------+----------+------------+

IN 关键字:判断 dept_id 是否与子查询返回的任意一行对应字段像匹配,可以将后者看作一个集合,通过 IN 来判断是否在集合内。

案例二:查询大于30号部门的最高薪资的其他部门的员工

sql 复制代码
--多行子查询
select * from emp where salary > all (select salary from emp where dept_id = 30);
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |
+--------+----------+--------+---------+------------+----------+------------+

--标量子查询
select * from emp where salary > (select max(salary) from emp where dept_id = 30);
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1001 | 李明     | 男     |    NULL |       NULL | 30000.00 | 2020-01-10 |
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |
+--------+----------+--------+---------+------------+----------+------------+

all 关键字:判断条件必须满足对集合内每一个元素都成立。

案例三:查询比 30 号部门任意其中一个员工高的其他部门的员工

sql 复制代码
select * from emp where salary > any (select salary from emp where dept_id= 30) and dept_id <> 30;
+--------+----------+--------+---------+------------+----------+------------+
| emp_id | emp_name | gender | dept_id | manager_id | salary   | hire_date  |
+--------+----------+--------+---------+------------+----------+------------+
|   1002 | 张伟     | 男     |      10 |       1001 | 22000.00 | 2021-03-15 |
|   1003 | 王芳     | 女     |      10 |       1002 | 16000.00 | 2022-06-01 |
|   1004 | 刘洋     | 男     |      10 |       1002 | 15000.00 | 2023-01-12 |
|   1005 | 陈静     | 女     |      10 |       1002 | 16000.00 | 2023-04-18 |
|   1006 | 赵强     | 男     |      20 |       1001 | 21000.00 | 2021-05-20 |
|   1007 | 孙丽     | 女     |      20 |       1006 | 14000.00 | 2022-08-22 |
|   1008 | 周杰     | 男     |      20 |       1006 | 12000.00 | 2023-03-09 |
|   1012 | 何林     | 男     |      40 |       1001 | 13000.00 | 2022-02-08 |
|   1014 | 钱涛     | 男     |      50 |       1001 | 19000.00 | 2021-09-16 |
|   1016 | 马悦     | 女     |      10 |       1002 | 15000.00 | 2024-05-06 |
+--------+----------+--------+---------+------------+----------+------------+
10 rows in set (0.00 sec)

any 关键字:判断条件只需满足集合中任意其中一个元素成立即可。

7.5 表子查询

案例:查询工资高于本部门平均工资的员工

sql 复制代码
select emp_name, salary, t1.dept_id, avg_sal 
from emp inner join (select dept_id, avg(salary) avg_sal fromrom emp where dept_id is not null group by dept_id) t1 
on emp.dept_id = t1.dept_id and salary > avg_sal;
+----------+----------+---------+--------------+
| emp_name | salary   | dept_id | avg_sal      |
+----------+----------+---------+--------------+
| 张伟     | 22000.00 |      10 | 16800.000000 |
| 赵强     | 21000.00 |      20 | 15666.666667 |
| 吴敏     | 18000.00 |      30 | 13333.333333 |
| 何林     | 13000.00 |      40 | 11000.000000 |
+----------+----------+---------+--------------+

上述 SQL 中,子查询返回的是多行多列的数据,它的查询结果可以作为一张逻辑表,可以通过内外连接,将两张表进行建立关联。

这种将子查询放在 FROM 子句中作为数据来源的查询方式,通过被称为表子查询。

相关推荐
重生之高冷汉堡包39 分钟前
什么是数据中心代理?原理拆解、类型区别、应用场景与选型指南
服务器·网络·数据库·安全·ip
Xxtaoaooo43 分钟前
NocoDB在线表格数据库:fnOS部署、MySQL可视化与远程协作实操
数据库·mysql·docker·cpolar·nocodb
张继雁1 小时前
30~50MPa数控高压清洗:前端分级过滤逻辑,抑制硬质磨粒对喷嘴冲蚀
大数据·数据库·人工智能·机器学习·创业创新
言乐61 小时前
PythonLOLM自动生成复盘报告模型
开发语言·前端·数据库·人工智能·python
倔强的石头_1 小时前
从 KStudio 到 KSQL Developer:国产数据库的 SQL 编辑器,已经卷到这个程度了
数据库
JavaEdge.1 小时前
设定索引方法和检索设置
数据库
小蒜学长1 小时前
基于Vue.js + Spring Boot的酒店客房预订系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·酒店客房预订系统
墨者阳1 小时前
KingbaseES V9R2C16 深度实测:64位事务ID、内存防护、集群备份与坏块修复四大能力全解析
数据库
事圆则缓1 小时前
聊天与引用回复的 SQL 怎么设计
数据库·sql