「MySQL」- 复杂的SQL查询语句

任意门

前言

聚合查询

1.聚合函数

count函数

sum函数

avg函数

max函数

min函数

2.分组查询

having子句

多表查询

1.笛卡尔积

2.内连接

3.外连接

左外连接

右外连接

自链接

子查询

合并查询

​ 关键字执行顺序

前言

MySQL 除了基础的 CRUD 操作之外 , 还涉及了更多更复杂的查询操作 , 不仅支持一张表进行查询 , 也支持多张表进行查询 ,本章是针对 MySQL 中的 select 查询操作更复杂的一些用法 , 在本章你将会学习进阶的 select 操作 , 随着作者一起往下看看吧(本章书写风格会在单行与多行之间切换) ! ! !

聚合查询

聚合查询是指对一个数据表中某个字段的数据进行部分或者全部统计查询的一种方式,比如所有全部书的平均价格或者是书籍的总数量等等 ,在这些时候就会使用到聚合查询这种方法。

在开始学习聚合查询之前 , 我们需要先在选中数据库中准备一张测试表 , 创建一张成绩表并添加测试记录

-- 创建成绩表
drop table if exists exam_result;
create table exam_result (
id int,
name varchar(20),
chinese decimal(3,1),
math decimal(3,1),
english decimal(3,1)
);
-- 插入测试数据
insert into exam_result (id,name, chinese, math, english) values
(1,'唐三藏', 67, 98, 56),
(2,'孙悟空', 87.5, 78, 77),
(3,'猪悟能', 88, 98.5, 90),
(4,'曹孟德', 82, 84, 67),
(5,'刘玄德', 55.5, 85, 45),
(6,'孙权', 70, 73, 78.5),
(7,'宋公明', 75, 65, 30),
(null,null,null,null,null);

接下来我们就可以开始学习聚合查询了 ! ! !

1.聚合函数

对于一张表中 , 万一有需求需要我们去计算整张表有多少条记录 , 或者指定一张表计算平均值/求和等操作 , 我们总不可能拿着个计算器在那边看表便计算吧 , 虽然可以但是个笨方法, 所以这时候就可以使用聚合函数来实现统计总数、计算平局值等操作 , 常见的聚合函数如下

函数 说明
count 返回查询到的数据的数量
sum 返回查询到的数据的总和,不是数字没有意义
avg 返回查询到的数据的平均值,不是数字没有意义
max 返回查询到的数据的最大值,不是数字没有意义
min 返回查询到的数据的最小值,不是数字没有意义
  • 每个聚合函数参数都可以为表达式或者列名

接下来 , 我们来看一下每一个聚合函数的用法

count函数

count 函数可以计算一张表中一共有多少条记录 , 并返回查询到的记录总数 , count 参数是什么类型都行

语法

select count(<表达式 / 列名>) from <数据表名>;

说明

  • count ( ) 中的参数可以是 * , 为 * 代表数据表中所有的记录

  • count ( ) 中的参数如果不是 * 的话 , 如果查询中没有格外的条件 , 那么记录值为 null 的将不计入查询结果

代码示例1

统计成绩表中一共有几条记录 , 参数为 * 的话 , 值为 null 的记录也会被计入到结果中

select count(*) from exam_result;

-- 查询结果
+----------+
| count(*) |
+----------+
|        8 |
+----------+
1 row in set (0.00 sec)

代码示例2

由于使用 * 参数会将值为 null 的记录也会被计入到结果 , 所以为了更精确的知道某一列中有多少条记录 , 我们就可以对 count 传入指定列来进行聚合查询 , 例如查询 name 列有几条记录 , 在使用指定列进行聚合查询的时候 , 会将该列值为 null 的值给忽略 , 不计入最终查询结果中

select count(name) from exam_result;

-- 查询结果
+-------------+
| count(name) |
+-------------+
|           7 |
+-------------+
1 row in set (0.00 sec)

sum函数

sum 函数可以对表中的指定列或者表达式进行一个求和 , 前提要求传入的参数列 / 表达式是数值类型

语法

select sum(表达式/列名) from <数据表名> [where];

说明

  • 要求传入的参数列 / 表达式是数值类型 , 否则没有意义

  • [ ]表示可选项 , 在使用 sum 函数时可以使用 where 子句

代码示例1

统计成绩表中的数学成绩总分

select sum(math) from exam_result;

-- 查询结果
+-----------+
| sum(math) |
+-----------+
|     581.5 |
+-----------+
1 row in set (0.00 sec)

代码示例2

统计成绩表中的数学成绩总分 , 要求对数学成绩大于 70 的分数进行求和 , 如果没有就返回 NULL , 这个时候我们就可以使用 where 子句进行条件筛选 , 增加条件 math > 70 , 这时候计算的就是大于 70 分以上的总分

select sum(math) from exam_result where math > 70;

-- 查询结果
+-----------+
| sum(math) |
+-----------+
|     516.5 |
+-----------+
1 row in set (0.00 sec)

如果对于筛选条件没有对应的结果的话 , 就会返回 NULL , 例如要求对数学成绩小于 60 的分数进行求和 , 从上面的总表就能看出 , 在数学成绩那一列是没有小于 60 分的 , 记录为 NULL 的也不会计算到结果当中 , 所以本次查询返回值就是 NULL

select sum(math) from exam_result where math < 60;

-- 查询结果
+-----------+
| sum(math) |
+-----------+
|      NULL |
+-----------+
1 row in set (0.00 sec)

代码示例3

参数不仅可以是指定列 , 还能指定表达式 , 前提是表达式的列都是数值类型 , 例如对 语文、数学、英语 全部成绩进行求和计算总分

select sum(chinese + math + english) from exam_result;

-- 查询结果
+-------------------------------+
| sum(chinese + math + english) |
+-------------------------------+
|                        1550.0 |
+-------------------------------+
1 row in set (0.00 sec)

avg函数

avg 函数可以对表中的指定列或表达式进行一个求平均值的操作 , 前提要求参数是数值类型

语法

select avg(表达式/列名) from <数据表名> [where];

说明

  • 要求传入的参数列 / 表达式是数值类型 , 否则没有意义

  • [ ]表示可选项 , 在使用 avg 函数时可以使用 where 子句

代码示例1

对成绩表中的数学成绩计算平均分

select avg(math) from exam_result;

-- 查询结果
+-----------+
| avg(math) |
+-----------+
|  83.07143 |
+-----------+
1 row in set (0.00 sec)

代码示例2

使用 where 子句对数学成绩大于 80 分的记录进行求平均分

select avg(math) from exam_result where math > 80;

-- 查询结果
+-----------+
| avg(math) |
+-----------+
|  91.37500 |
+-----------+
1 row in set (0.00 sec)

代码示例3

传入参数是一个表达式 , 对三门课程总分统计平均分

select avg(chinese + math + english) from exam_result;

-- 查询结果
+-------------------------------+
| avg(chinese + math + english) |
+-------------------------------+
|                     221.42857 |
+-------------------------------+
1 row in set (0.00 sec)

max函数

max 函数是返回表中的指定列中的最大值 , 前提要求该列为数值类型

语法

select max(列名) from <数据表名> [where];

说明

  • 要求传入的参数列是数值类型 , 否则没有意义

  • [ ]表示可选项 , 在使用 max 函数时可以使用 where 子句

代码示例1

查询数学成绩最高分

select max(math) from exam_result;

-- 查询结果
+-----------+
| max(math) |
+-----------+
|      98.5 |
+-----------+
1 row in set (0.00 sec)

代码示例2

查询数学成绩小于 80 分的记录中的最高分

select max(math) from exam_result where math < 80;

-- 查询结果
+-----------+
| max(math) |
+-----------+
|      78.0 |
+-----------+
1 row in set (0.00 sec)

min函数

min 函数是返回表中的指定列中的最大值 , 前提要求该列为数值类型

语法

select min(列名) from <数据表名> [where];

说明

  • 要求传入的参数列是数值类型 , 否则没有意义

  • [ ]表示可选项 , 在使用 min 函数时可以使用 where 子句

代码示例1

查询成绩表中 数学成绩 的最低分

select min(math) from exam_result;

-- 查询结果
+-----------+
| min(math) |
+-----------+
|      65.0 |
+-----------+
1 row in set (0.00 sec)

代码示例2

查询成绩表中 数学成绩大于 80 分的最低分

select min(math) from exam_result where math > 80;
  
-- 查询结果
+-----------+
| min(math) |
+-----------+
|      84.0 |
+-----------+
1 row in set (0.00 sec)

聚合函数注意事项

  • 以上介绍的聚合函数中除了 count 函数 , 其它 sum / avg / max / min 函数都要求参数列是数值类型

  • 聚合函数是可以一起使用的

  • where 子句不能对聚合查询后的数据进行筛选 , 这个下文 having 子句会讲到

2.分组查询

分组查询属于是聚合查询中的一个重要用法 , 直接使用聚合函数相当于是把所有的数据都聚合在一起 , 但是一般来讲 , 有些场景下我们是需要将一张表进行分组后在使用聚合函数对分组后的数据进行查询 , 所以就会涉及到 "分组聚合" , 分组聚合的含义,就是根据某个列的值进行分组,把值相同的行分到一组去.然后再针对每个组分别执行聚合函数

在了解分组查询之前 , 我们需要准备另一张员工表作为测试表 , 并给这张员工表进行添加测试数据

create table emp(
	id int primary key auto_increment,
	name varchar(20) not null,
	role varchar(20) not null,
	salary numeric(11,2)
);
-- 添加测试数据
insert into emp(name, role, salary) values
('马云','服务员', 1000.20),
('马化腾','游戏陪玩', 2000.99),
('孙悟空','游戏角色', 999.11),
('猪无能','游戏角色', 333.5),
('沙和尚','游戏角色', 700.33),
('隔壁老王','董事长', 12000.66);

group by子句

group by 是分组查询的关键字 , 使用 group by 可以对指定列进行分组查询 , 一般要搭配聚合函数使用

语法

select <聚合函数()> from <数据表> group by <分组条件>;

代码示例1

  • 题目 : 对员工表查询每个岗位的最高工资

针对每个岗位 , 我们可以将 group by 的条件设置为员工表的岗位类型字段进行分组 ,对于查询这种最高或者最大的值 , 一般用 max 聚合函数 , ,为了数据表更详细 , 我们可以在指定列中添加岗位表一起展示

select role , max(salary) from emp group by role;

-- 查询结果
+--------------+-------------+
| role         | max(salary) |
+--------------+-------------+
| 服务员       |     1000.20 |
| 游戏角色     |      999.11 |
| 游戏陪玩     |     2000.99 |
| 董事长       |    12000.66 |
+--------------+-------------+
4 rows in set (0.00 sec

可以看到 , select 语句对我们使用 role 列作为 group by 条件 , 会自动按照条件 , 对于相同的岗位会分到一组 , 然后再对每一组进行聚合查询 , 得到最终分组聚合查询的结果

代码示例2

  • 题目 : 查询每个岗位的最高工资、最低工资和平均工资

我们依旧可以根据 role 列来作为 group by 子句的条件进行分组 , 然后在针对工资分别使用 max min avg 聚合函数来进行计算

select role,max(salary),min(salary),avg(salary) from emp group by role;

-- 查询结果
+--------------+-------------+-------------+--------------+
| role         | max(salary) | min(salary) | avg(salary) |
+--------------+-------------+-------------+--------------+
| 服务员       |     1000.20 |     1000.20 |  1000.200000 |
| 游戏角色     |      999.11 |      333.50 |   677.646667 |
| 游戏陪玩     |     2000.99 |     2000.99 |  2000.990000 |
| 董事长       |    12000.66 |    12000.66 | 12000.660000 |
+--------------+-------------+-------------+--------------+
4 rows in set (0.00 sec)

having子句

在使用 group by 子句进行分组后 , 如果想对聚合分组后的条件进行筛选时 , 由于在前面我们学习了 where 子句 , 此时我们第一个想法肯定是使用 where 子句来进行条件筛选 , 但是在 where 子句中的条件是针对当前表中的原始数据来进行筛选的 , 我们期望的是针对聚合后的数据进行筛选 , 那么就需要用 having 子句 , having 子句的使用跟 where 子句使用一样 , 如果条件不是聚合函数或者聚合后的数据 , 那么 havingwhere 没区别

语法

select <聚合函数()> from <数据表名> group by <分组条件> having <条件>;

代码示例1

  • 题目 : 对各岗位计算平均工资 , 过滤平均工资小于 1500 的岗位

我们先看看过滤前的分组聚合查询的记录

select role,avg(salary) from emp group by role;

-- 查询结果
+--------------+--------------+
| role         | avg(salary)  |
+--------------+--------------+
| 服务员       |  1000.200000 |
| 游戏角色     |   677.646667 |
| 游戏陪玩     |  2000.990000 |
| 董事长       | 12000.660000 |
+--------------+--------------+
4 rows in set (0.00 sec)

对于这种需要过滤聚合函数计算后的记录 , 就可以使用 having 来进行过滤 , 将平均工资大于 1500 作为条件 , 过滤掉平均工资小于 1500 的岗位

select role,avg(salary) from emp group by role having avg(salary) > 1500;

-- 查询结果
+--------------+--------------+
| role         | avg(salary)  |
+--------------+--------------+
| 游戏陪玩     |  2000.990000 |
| 董事长       | 12000.660000 |
+--------------+--------------+
2 rows in set (0.00 sec)

代码示例2

where 也能和 group by 搭配使用 , where 是针对聚合前的数据进行筛选 , having 是针对聚合后的数据进行筛选 , 两个子句可以搭配使用 , 例如

  • 题目 : 找到所有平均薪资大于 1500 的岗位 , 过滤掉董事长岗位

对于这种题目 , 我们就可以使用 where 子句过滤掉 "董事长" 然后使用 group by 先对岗位进行一个聚合分组 , 最后在对聚合分组的平均工资使用 having 进行过滤掉小于 1500 的岗位

select role,avg(salary)
from emp
where role != '董事长'
group by role
having avg(salary) > 1500;

-- 查询结果
+--------------+-------------+
| role         | avg(salary) |
+--------------+-------------+
| 游戏陪玩     | 2000.990000 |
+--------------+-------------+
1 row in set (0.00 sec)

group by 分组的过程 , 本质上还是需要针对表进行遍历.根据指定列 role 的值,来把当前记录归到某个组中了.如果搭配了where role != '董事长',意味着当遇到 董事长 这条记录的时候这条记录就不会被归入任何分组 , 满足 where 条件的记录,才会进入分组

多表查询

在原先我们学习的都是针对一张表进行查询 , 实际开发中往往数据来自不同的表,所以需要多表联合查询。

1.笛卡尔积

多表查询是对多张表的数据取笛卡尔积 , 所谓的笛卡尔积就是将多张表中的记录全部组合可能性枚举到一张临时表当中 , 如果没有任何条件限制 , 那么在这张临时表中的记录总数 = (表A * 表B)的记录

  • 新表的列数,相当于原来两张表的列数之和.

  • 新表的行数,相当于原来两张表的行数之积.

语法

select <列名> from 数据表1 , 数据表2,....; 
  • 多张表使用 逗号 分割

用代码来简单了解一下 笛卡尔积 的操作 , 先创建三张测试表

  • student 学生表 , 表中字段有 : id(自增主键) 序号 , name 学生名

    create table student(
        id int primary key auto_increment,
        name varchar(20)
    );
  • course 课程表 , 表中字段有 : id(自增主键) 课程序号 , name 课程名

    create table course(
    	id int primary key auto_increment,
        name varchar(20)
    );
  • scores 成绩表 , 表中字段有 : score 分数 , student_id 对应学生表 id , course_id 对应课程表 id

    create table scores(
    	score decimal(3,1),
    	student_id int,
    	course_id int
    );
  • 对三张表添加测试记录

    -- 对 student 表添加记录
    insert into student(name) values
    ('张三'),
    ('李四'),
    ('王五'),
    ('赵六'),
    ('孙七');
    
    -- 对 course 表添加记录
    insert into course(name) values
    ('语文'),
    ('数学'),
    ('英语');
    
    -- 对 scores 表添加记录
    insert into scores values
    (70.5,1,1),(80.5,1,2),(95,1,3),-- 张三
    (98.5,2,1),(86,2,2),(64,2,3),-- 李四
    (81.0,3,1),(95.0,3,2),(86.0,3,3),-- 王五
    (79.0,4,1),(73.5,4,2),(88,4,3),-- 赵六
    (76.5,5,1),(91,5,2),(79,5,3);-- 孙七

接下来我们来对学生表和分数表进行一个笛卡尔积的操作 , 由于笛卡尔积的操作会返回大量的数据 , 所以这里采用 limit 关键字限制一下查询的记录 , 看一下笛卡尔积会发生什么

select * from student,scores limit 10;

-- 查询结果
+----+--------+-------+------------+-----------+
| id | name   | score | student_id | course_id |
+----+--------+-------+------------+-----------+
|  1 | 张三   |  70.5 |          1 |         1 |
|  2 | 李四   |  70.5 |          1 |         1 |
|  3 | 王五   |  70.5 |          1 |         1 |
|  4 | 赵六   |  70.5 |          1 |         1 |
|  5 | 孙七   |  70.5 |          1 |         1 |
|  1 | 张三   |  80.5 |          1 |         2 |
|  2 | 李四   |  80.5 |          1 |         2 |
|  3 | 王五   |  80.5 |          1 |         2 |
|  4 | 赵六   |  80.5 |          1 |         2 |
|  5 | 孙七   |  80.5 |          1 |         2 |
+----+--------+-------+------------+-----------+
10 rows in set (0.00 sec)

从笛卡尔积得到的临时表10条记录当中可以看到 , 对两张表进行笛卡尔积 , 是将两张表中的记录进行枚举组合一遍 , 在进行笛卡尔后也会得到很多无效记录 , 虽然才显示 10 条记录 , 无效记录就有 8 条 , 比如临时表中的 张三 同学对应的 student_id 应该是 1 , 在经过笛卡尔积后 , 临时表中又多出来多条同学对应的 studentId 不为 1  的记录 , 所以在进行笛卡尔积操作时一般都会加上条件筛选。

笛卡尔积对两张表的链接操作又可以细分为 内连接 左外连接 右外连接 , 一起往下看看叭

2.内连接

对于上面的笛卡尔积 , 会出现大量的无效记录 , 针对无效记录进行筛选就是设置连接条件 , 而笛卡尔积中的内连接是通过在查询中设置连接条件的方式来移除查询结果集中某些数据行后的交叉连接 , 通俗点来说就是将两张表使用条件筛选出对应的记录 , 一般都是找两张表中的 对应列 作为等值条件 , 如下图所示 , 将两张表中的对应的数据给展现出来

语法

对于内连接 , 一共有两种方式

select <列名> from <数据表名1>,<数据表名2>,... where <链接条件>;
select <列名> from <数据表1> [inner] join <数据表2> on <链接条件>;
  • inner 是内连接的关键字 , 默认就是内连接 , 可以省略 , 但是链接条件不写的话就是笛卡尔积了

代码示例1

  • 题目 : 查看所有学生的分数

需要用到学生表和分数表 , 对于两张表我们可以看到 student 表中的 id 字段是可以对应上 scores 表的 studentId 的 , 所以我们可以将 id = studentId 作为连接条件 , 为了区分是哪张表的字段 , 我们可以在字段前面加上 表名. , 例如 id可以写成 student.id

select name,score
from student,scores
where student.id = scores.student_id;

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |  70.5 |
| 张三   |  80.5 |
| 张三   |  95.0 |
| 李四   |  98.5 |
| 李四   |  86.0 |
| 李四   |  64.0 |
| 王五   |  81.0 |
| 王五   |  95.0 |
| 王五   |  86.0 |
| 赵六   |  79.0 |
| 赵六   |  73.5 |
| 赵六   |  88.0 |
| 孙七   |  76.5 |
| 孙七   |  91.0 |
| 孙七   |  79.0 |
+--------+-------+
15 rows in set (0.00 sec)

对于第二种写法也是一样的

select name,score
from student
join scores on student.id = scores.student_id;

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |  70.5 |
| 张三   |  80.5 |
| 张三   |  95.0 |
| 李四   |  98.5 |
| 李四   |  86.0 |
| 李四   |  64.0 |
| 王五   |  81.0 |
| 王五   |  95.0 |
| 王五   |  86.0 |
| 赵六   |  79.0 |
| 赵六   |  73.5 |
| 赵六   |  88.0 |
| 孙七   |  76.5 |
| 孙七   |  91.0 |
| 孙七   |  79.0 |
+--------+-------+
15 rows in set (0.00 sec)

可以看到通过设置链接条件 , 可以将两张表中的对应的记录一起展示出来 , 并且没有多余的无效记录

代码示例2

  • 题目 : 查询 "张三" 同学的分数

对于这种题目 , 我们不仅要设置链接条件 , 这里的链接条件也是 id=studentId , 还需要添加其它条件 , 对于链接条件和其他条件要同时筛选 , 我们可以使用 and 逻辑运算符 , 在 and 后面加个 name="张三" 的条件进行筛选

select name,score
from student
join scores on student.id = scores.student_id and student.name = '张三';

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |  70.5 |
| 张三   |  80.5 |
| 张三   |  95.0 |
+--------+-------+
3 rows in set (0.00 sec)

当然也可以将 join on 作为链接条件 , 使用 where 作为其它条件 , 这样代码会更易读

select name,score
from student
join scores on student.id = scores.student_id 
where student.name = '张三';

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |  70.5 |
| 张三   |  80.5 |
| 张三   |  95.0 |
+--------+-------+
3 rows in set (0.00 sec)

代码示例3

  • 查看所有同学对应的课程成绩 , 并展示课程信息

由于学生 , 课程 , 成绩都在不同的表中 , 所以这道题目要涉及到三张表进行笛卡尔积内连接查询 , 所以我们要先找到三张表之间的关联 , 而在 scores 表当中有两个字段 student_id 和 course_id 对应的就是 student 表中的idcourse 表中的 id , 所以我们将链接条件设置为 scores.student_id = student.id and scorese.course_id = course.id , 对于 course 表中的 name 字段和 student 表中的 name 字段冲突了 , 我们也可以使用 数据表名.字段 来区分

select student.name,course.name,scores.score
from student,course,scores
where scores.student_id = student.id and scores.course_id = course.id;
	
-- 查询结果
+--------+--------+-------+
| name   | name   | score |
+--------+--------+-------+
| 张三   | 语文   |  70.5 |
| 张三   | 数学   |  80.5 |
| 张三   | 英语   |  95.0 |
| 李四   | 语文   |  98.5 |
| 李四   | 数学   |  86.0 |
| 李四   | 英语   |  64.0 |
| 王五   | 语文   |  81.0 |
| 王五   | 数学   |  95.0 |
| 王五   | 英语   |  86.0 |
| 赵六   | 语文   |  79.0 |
| 赵六   | 数学   |  73.5 |
| 赵六   | 英语   |  88.0 |
| 孙七   | 语文   |  76.5 |
| 孙七   | 数学   |  91.0 |
| 孙七   | 英语   |  79.0 |
+--------+--------+-------+
15 rows in set (0.00 sec)

3.外连接

外连接分为左外连接和右外连接 , 如果多表查询 , 左侧的表完全显示我们就说是左外连接 , 右侧的表完全显示我们就说是右外连接。

为了更好的了解外连接 , 我们在这还需要先插入两条测试记录

  • student 表添加一条记录 , 不对 name 添加数据

    insert into student values(6,'周八');
  • scores 添加一条记录

    insert into scores(score) values(88.0);

这样这两张表就有一条记录对应不上了 , 我们先对两张表进行内连接查看一下

select name,score 
from student
join scores on student.id = scores.student_id;

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |  70.5 |
| 张三   |  80.5 |
| 张三   |  95.0 |
| 李四   |  98.5 |
| 李四   |  86.0 |
| 李四   |  64.0 |
| 王五   |  81.0 |
| 王五   |  95.0 |
| 王五   |  86.0 |
| 赵六   |  79.0 |
| 赵六   |  73.5 |
| 赵六   |  88.0 |
| 孙七   |  76.5 |
| 孙七   |  91.0 |
| 孙七   |  79.0 |
+--------+-------+
15 rows in set (0.00 sec)

可以看到 , 进行内连接查询时 , 刚才插入的两条记录没对应上 , 所以在内连接查询结果当中并不包含刚刚插入的记录 , 所以想将不对应的数据也展现出来的话 , 就要使用外连接 , 接下来看一下外连接是怎么操作的

左外连接

左外连接又称为左连接 , 在 左外链接 得到的临时表当中 , 除了匹配对应的行之外 , 还会尽可能的将左边的表中与右边的表中不匹配的信息给展现出来 , 如下图所示 , 将两张表中除了对应的数据给展现出来之外 , 还将左边表中的数据一起展现出来

语法

select <列名> from <数据表1> left join <数据表2> on <连接条件>;
  • left是左外连接关键字 , 最终查询结果,是以 join 左侧的表为主,会尽可能的把左侧的表的所有信息都体现出来 

代码示例

  • 题目 : 查看所有同学的成绩 , 如果该同学没有成绩也需要显示

student 表作为左表 , scores 表作为右表 , 这样进行左外连接笛卡尔积的时候就会将 student 表中的信息全部展现出来 , 即使有记录不对应 scores 表中的记录 , 对应的列就会默认填 NULL

select name,score
from student
left join scores on student.id = scores.student_id;

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |  70.5 |
| 张三   |  80.5 |
| 张三   |  95.0 |
| 李四   |  98.5 |
| 李四   |  86.0 |
| 李四   |  64.0 |
| 王五   |  81.0 |
| 王五   |  95.0 |
| 王五   |  86.0 |
| 赵六   |  79.0 |
| 赵六   |  73.5 |
| 赵六   |  88.0 |
| 孙七   |  76.5 |
| 孙七   |  91.0 |
| 孙七   |  79.0 |
| 周八   |  NULL |
+--------+-------+
16 rows in set (0.00 sec)

右外连接

右外连接又称为右连接 , 除了展示匹配对应的记录之外 , 还会尽可能的将右边的表中与左边边的表中不匹配的信息给展现出来 , 跟左外连接差不多 , 不过一个是展示左表的全部信息 , 一个是展示右表的所有信息 , 如下图所示

语法

select <列名> from <数据表1> right join <数据表2> on <链接条件>;
  • right 是右链接查询的关键字 , 和左外连接类似,是以 join右侧的表为主,尽可能把右侧表的每个信息都体现出来.

代码示例

  • 题目 : 查看所有的分数及对应的同学 , 如果分数没有对应的学生也需要显示

student 表作为左表 , scores 表作为右表 , 使用右外连接查询就可以就将 scores 表中所有的记录给展示出来

select name,score 
from student 
right join scores on student.id = scores.student_id;

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |  70.5 |
| 张三   |  80.5 |
| 张三   |  95.0 |
| 李四   |  98.5 |
| 李四   |  86.0 |
| 李四   |  64.0 |
| 王五   |  81.0 |
| 王五   |  95.0 |
| 王五   |  86.0 |
| 赵六   |  79.0 |
| 赵六   |  73.5 |
| 赵六   |  88.0 |
| 孙七   |  76.5 |
| 孙七   |  91.0 |
| 孙七   |  79.0 |
| NULL   |  88.0 |
+--------+-------+
16 rows in set (0.00 sec)

ps : 左外链接能做到的查询 , 右外连接也能对应查询 , 相反右外链接能做到的查询 , 左外连接也能对应查询 , 只需要将两张表的顺序交换即可 , 得到的结果是一样的

自链接

自链接属于 SQL中的"奇淫巧计" , 本质是把一张表当作两张来使用 , 也就是自己和自己做笛卡尔积 , 在进行自链接时需要给表设置别名

在 MySQL 中比较方便的是字段与字段中进行比较 , 也就是列与列之间进行比较 , 但要想行与行之间进行并比较是比较困难的 , 比如上面创建的成绩表 , 对于不同的课程 , 成绩是行与行之间进行展示的 , 如果想进行课程成绩比较是比较困难的 , 比如以下题目

  • 查询出所有语文成绩小于数学成绩的记录 , 语文课程id为1 , 数学课程id为2

我们可以使用自链接将行与行之间的比较转换成列于列之间的比较 , 对于这种复杂的 SQL 语句我们可以分步骤执行 SQL 语句 , 我们可以先使用内连接查询 , 建立两张表的链接 , 不要忘了给表名取别名 , 我们可以使用 limit 限制查询记录 , 然后再从结果中找到出筛选条件在进行下一步的 SQL

select * 
from scores as s1 
join scores as s2 on s1.student_id = s2.student_id limit 15;

查询结果

可以看到 , 在内连接查询表中课程 id1 和 课程 id2 的有同一行的记录的 , 所以我们下一步 SQL 语句只需要将课程 id1 和 课程 id2 的记录筛选出来进行分数比较即可得到最终结果

select * 
from scores as s1 
join scores as s2 on s1.student_id = s2.student_id
where s1.course_id = 1 and s2.course_id = 2 and s1.score < s2.score;

-- 查询结果
+-------+------------+-----------+-------+------------+-----------+
| score | student_id | course_id | score | student_id | course_id |
+-------+------------+-----------+-------+------------+-----------+
|  70.5 |          1 |         1 |  80.5 |          1 |         2 |
|  81.0 |          3 |         1 |  95.0 |          3 |         2 |
|  76.5 |          5 |         1 |  91.0 |          5 |         2 |
+-------+------------+-----------+-------+------------+-----------+
3 rows in set (0.00 sec)

注意 : 在进行自链接时 , 需要给数据表设置别名 , 否则会报错

子查询

子查询是嵌套在另一个SQL语句中的查询 , 也可以称为嵌套查询 , 将内部查询的结果返回给外部的SQL语句 , 一般用内部查询的结果作为外部查询的条件 , where 型子查询把内层查询结果当作外层查询的比较条件,简单来说就是套娃

先插入两条重复数据 , 方便下面的代码示例

insert into scores values
(70.5,1,1),(80.5,1,2),(95,1,3),-- 张三
(98.5,2,1),(86,2,2),(64,2,3);-- 李四

代码示例1

  • 题目 : 查询王五同学的语文成绩

前面我们知道 语文 课程的 id1 , 所以我们查询的时候可以直接对分数表中的 course_id 字段进行筛选条件内连接查询 , 就很容易得到结果

select student.name,scores.score 
from student
join scores on student.id = scores.student_id
where name = '王五' and scores.course_id = 1;

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 王五   |  81.0 |
+--------+-------+
1 row in set (0.00 sec)

那么 , 假设我们不知道 语文 课程 id 的情况下 , 该怎么去查询 ? 按照正常的想法就是要进行分批查询 , 首先我们先在课程表通过课程名查询出课程 id

select id from course where name = '语文';

-- 查询结果
+----+
| id |
+----+
|  1 |
+----+
1 row in set (0.00 sec)

知道课程 id 后 , 然后我们再去进行查成绩的 SQL 语句

select student.name,scores.score 
from student
join scores on student.id = scores.student_id
where name = '王五' and scores.course_id = 1;

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 王五   |  81.0 |
+--------+-------+
1 row in set (0.00 sec)

但是 , MySQL 中的子查询就可以将以上分批查询的两条语句合并成一条语句 , 子查询的功能就是将一条 SQL 语句的结果返回给另一条 SQL 语句 , 所以我们可以将查询课程 idSQL 语句的结果 作为 查询成绩的 SQL 语句条件中的值 , 一样可以得到正确查询结果

select student.name,scores.score 
from student
join scores on student.id = scores.student_id
where name = '王五' and scores.course_id = (select id from course where name = '语文' );

-- 查询结果
+--------+-------+
| name   | score |
+--------+-------+
| 王五   |  81.0 |
+--------+-------+
1 row in set (0.00 sec)

代码示例2

  • 题目 : 查询重复的成绩

这道题我们需要去针对成绩表中的所有记录进行一个分组计数 , 可以使用聚合函数 count 配合分组 group by 实现 , 对于重复记录可以在分组聚合查询后增加对 聚合函数 的筛选条件 , 将总数大于 1 的记录给展示出来 , 再将记录返回给查询重复成绩的 SQL 语句。

在查询重复记录 SQL 语句中 , 我们需要对 score,student_id,course_id 来对返回结果进行等值判断 , 由于返回回结果可能有多个 , 所以我们使用 in 运算符来进行条件筛选

select * 
from scores
where (score,student_id,course_id) in (
	select *
	from scores
	group by student_id,course_id having count(*) > 1
);

-- 查询结果
+-------+------------+-----------+
| score | student_id | course_id |
+-------+------------+-----------+
|  70.5 |          1 |         1 |
|  80.5 |          1 |         2 |
|  95.0 |          1 |         3 |
|  98.5 |          2 |         1 |
|  86.0 |          2 |         2 |
|  64.0 |          2 |         3 |
|  70.5 |          1 |         1 |
|  80.5 |          1 |         2 |
|  95.0 |          1 |         3 |
|  98.5 |          2 |         1 |
|  86.0 |          2 |         2 |
|  64.0 |          2 |         3 |
+-------+------------+-----------+
12 rows in set (0.00 sec)

合并查询

MySQL中可以使用使用集合操作符 union,union all合并多个 select 的执行结果 ,union 和 union all 时,前后查询的结果集中,字段需要一致

语法

select <列名> from 数据表名 [where] <条件>
union/union all
select <列名> from 数据表名 [where] <条件>;

示例

第一个查询语句查询 student 表中 id 字段小于 3 的记录 , 第二个查询语句查询 student 表中 id 字段小于 5 的记录 , 使用合并查询将两张表进行合并

select * from student where id < 3
union all
select * from student where id < 5;

-- 查询结果
+----+--------+
| id | name   |
+----+--------+
|  1 | 张三   |
|  2 | 李四   |
|  1 | 张三   |
|  2 | 李四   |
|  3 | 王五   |
|  4 | 赵六   |
+----+--------+
6 rows in set (0.00 sec)

以上是集合操作符 union all 的使用 , 我们使用 union 看看有什么区别

select * from student where id < 3
union
select * from student where id < 5;

-- 查询结果
+----+--------+
| id | name   |
+----+--------+
|  1 | 张三   |
|  2 | 李四   |
|  3 | 王五   |
|  4 | 赵六   |
+----+--------+
4 rows in set (0.00 sec)

可以看到 , 使用 union all 的时候会将两张表中的重复记录一起展示出来 , 而使用 union 的时候会去掉重复行可达到去重效果

 关键字执行顺序

在文章的最后 , 给大家分享一下SQL 查询语句中的各个关键字的执行的先后顺序

1 from
2 on
3 join
4 where
5 group by
6 with
7 having
8 select
9 distinct
10 order by
11 limit

 本章到此结束,如果文中有写的不对或不懂的地方,欢迎评论区讨论,谢谢!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>