文章目录
- 前言
- Ⅰ. 创建新数据
- 1、`insert`语句
- 2、插入否则更新 -- 替换
- 3、替换 -- `replace`
- Ⅱ. 检索数据
- 1、`select`语句
- ① 全列查询
- ② 指定列查询
- ③ 查询字段为表达式
- ④ 为查询结果指定别名 `as`
- ⑤ 结果去重 `distinct`
- 2、`where` 条件
- 🎏 `where` 条件在 `sql` 语句中的执行顺序
- 比较运算符
- 逻辑运算符
- 使用案例
- ① 英语不及格的同学及英语成绩,即小于60分
- ② 语文成绩在 [80, 90] 分的同学及语文成绩
- ③ 数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩
- ④ 姓孙的同学及孙某同学
- ⑤ 语文成绩好于英语成绩的同学
- ⑥ 总分在 200 分以上的同学
- ⑦ 语文成绩 > 80 并且不姓孙的同学
- ⑧ 猪某同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80
- ⑨ NULL 的查询
- 3、`order by` 结果排序
- 🎏`order by`子句在 `sql` 语句中的执行顺序
- 使用案例
- ① 同学及数学成绩,按数学成绩升序显示
- ② 同学及 qq 号,按 qq 号排序显示
- ③ 查询同学各门成绩,依次按 数学降序,英语升序,语文升序的方式显示
- ④ 查询同学及总分,由高到低
- ⑤ 查询姓孙的同学或者姓曹的同学数学成绩,结果按数学成绩由高到低显示
- 4、`limit` 筛选分页结果
- 🎏 `limit` 子句在 `sql` 语句中的执行顺序
- Ⅲ. 更新数据
- 1、`update`语句
- 2、使用案例
- ① 将孙悟空同学的数学成绩变更为 80 分
- ② 将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分
- ③ 将总成绩倒数前三的 3 位同学的数学成绩加上 30 分
- ④ 将所有同学的语文成绩更新为原来的 2 倍
- Ⅳ. 删除数据
- 1、`delete`删除数据
- ① 删除孙悟空同学的考试成绩
- ② 删除整张表数据
- 2、`truncate`截断表
- ① 删除整张表数据
- 3、两者的区别
- Ⅴ. 插入查询结果
- 1、语法
- 2、案例
- Ⅵ. 聚合函数
- 1、常见聚合函数
- 2、案例
- ① 统计班级共有多少同学
- ② 统计班级收集的数学成绩有多少
- ③ 统计本次考试的数学成绩分数去重后的个数
- ④ 统计数学成绩总分
- ⑤ 统计平均总分
- ⑥ 返回英语最高分
- ⑦ 返回 > 70 分以上的数学最低分
- Ⅶ. `group by`分组查询 && `having` 结果过滤
- 1、`group by`语法
- 案例准备工作
- 2、`having`语法
- 3、案例
- ① 显示每个部门的每种岗位的平均工资和最低工资
- ② 显示平均工资低于2000的部门和它的平均工资
- 实战OJ题
前言
一般来说,数据库的基本操作叫做 CRUD,是一种常见的编程术语,它代表了四个基本的数据库操作:创建(Create)、读取(Read)、更新(Update)和删除(Delete)。这些操作用于对数据库中的数据进行管理。
上面说的四种基本操作,是对四种操作的总览,具体使用的语句细分下去是有差别的,下面我们分别来学习它们!
SQL查询中各个关键字的执行先后顺序: from > on> join > where > group by > with > having > select> distinct > order by > limit。
Ⅰ. 创建新数据
1、insert语句
insert [into] table_name
[(列名, 列名, ...)]
values (value_list) [, (value_list), ...] 其中:value_list: value, [, value, ...]
- 选项说明:
- 其中方括号表示可选项
column表示列字段value表示对应各自column要填入的值- 插入新数据的时候,
column和value一定要一一对应
根据 column 和 value_list 的个数不同,可以划分为下面的情况:

2、插入否则更新 – 替换
由于 主键 或者 唯一键 对应的值已经存在而导致插入失败:
-- 主键冲突
insert into students (id, sn, name) values (1, 10010, 'lirendada');
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'-- 唯一键冲突
insert into students (sn, name) values (102, 'james');
ERROR 1062 (23000): Duplicate entry '20001' for key 'sn'
我们可以选择性的进行同步更新操作语法:
insert ... on DUPLICATE key update column = value [, column = value, ...]
其中省略号代表的是 insert 语句的语法,当插入冲突的时候才会触发后面的这些语法!

其中在我们插入数据之后经常会有下面三个情况:
0 row affected:表中有冲突数据,但冲突数据的值和update的值相等,相当于没更新。1 row affected:表中没有冲突数据,数据被插入。2 row affected:表中有冲突数据,并且数据已经被更新。
3、替换 – replace
其实这和上面的操作是一样的,只不过有了 replace 这个语句专门来解决这种下面的情况:
- 主键 或者 唯一键 没有冲突,则直接插入
- 主键 或者 唯一键 如果冲突,则删除后再插入
其语法如下所示:
replace into 表名 (要插入的列字段) values (列字段对应的值);
举个例子:

Ⅱ. 检索数据
1、select语句
select 语句是一种用于从数据库中检索数据的 SQL 语句。它允许我们 指定要检索的列和表,并可以使用条件来过滤结果。
select[distinct] # 去重{* 或者 {列名 [, 列名] ...} [from] 表名 # 要检索的表名[where ...] # 用于指定条件来过滤结果[order by column [asc | desc], ...] # 排序[limit ...] # 限定筛选条数
- 参数说明:
- 其中 方括号的内容代表可以省略
select语句的选项不止有上面这些,后面会慢慢介绍到其它的!
下面我们创建的表结构这里直接给出来,因为不是这里的重点:
-- 创建表结构
CREATE TABLE exam_result (id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,name VARCHAR(20) NOT NULL COMMENT '同学姓名',chinese float DEFAULT 0.0 COMMENT '语文成绩',math float DEFAULT 0.0 COMMENT '数学成绩',english float DEFAULT 0.0 COMMENT '英语成绩'
);-- 插入测试数据
INSERT INTO exam_result (name, chinese, math, english) VALUES('唐三藏', 67, 98, 56),('孙悟空', 87, 78, 77),('猪悟能', 88, 98, 90),('曹孟德', 82, 84, 67),('刘玄德', 55, 85, 45),('孙权', 70, 73, 78),('宋公明', 75, 65, 30);
① 全列查询
通常情况下 不建议使用 * 进行全列查询,原因如下所示:
-
查询的列越多,意味着需要传输的数据量越大。
-
可能会影响到索引的使用。(索引待后面讲解)
mysql> select * from exam_result;
+----+-----------+---------+------+---------+
| id | name | chinese | math | english |
+----+-----------+---------+------+---------+
| 1 | 唐三藏 | 67 | 98 | 56 |
| 2 | 孙悟空 | 87 | 78 | 77 |
| 3 | 猪悟能 | 88 | 98 | 90 |
| 4 | 曹孟德 | 82 | 84 | 67 |
| 5 | 刘玄德 | 55 | 85 | 45 |
| 6 | 孙权 | 70 | 73 | 78 |
| 7 | 宋公明 | 75 | 65 | 30 |
+----+-----------+---------+------+---------+
7 rows in set (0.00 sec)
② 指定列查询
指定列的顺序不需要按定义表的顺序来,可以按照查询的顺序来显示。记得 指定列中间用逗号分隔开!
mysql> select id,chinese,name,math from exam_result;
+----+---------+-----------+------+
| id | chinese | name | math |
+----+---------+-----------+------+
| 1 | 67 | 唐三藏 | 98 |
| 2 | 87 | 孙悟空 | 78 |
| 3 | 88 | 猪悟能 | 98 |
| 4 | 82 | 曹孟德 | 84 |
| 5 | 55 | 刘玄德 | 85 |
| 6 | 70 | 孙权 | 73 |
| 7 | 75 | 宋公明 | 65 |
+----+---------+-----------+------+
7 rows in set (0.00 sec)
③ 查询字段为表达式
-- 表达式不包含字段
mysql> select id,name,10 from exam_result;
+----+-----------+----+
| id | name | 10 |
+----+-----------+----+
| 1 | 唐三藏 | 10 |
| 2 | 孙悟空 | 10 |
| 3 | 猪悟能 | 10 |
| 4 | 曹孟德 | 10 |
| 5 | 刘玄德 | 10 |
| 6 | 孙权 | 10 |
| 7 | 宋公明 | 10 |
+----+-----------+----+
7 rows in set (0.00 sec)-- 表达式包含一个字段
mysql> select id,name,10+math from exam_result;
+----+-----------+---------+
| id | name | 10+math |
+----+-----------+---------+
| 1 | 唐三藏 | 108 |
| 2 | 孙悟空 | 88 |
| 3 | 猪悟能 | 108 |
| 4 | 曹孟德 | 94 |
| 5 | 刘玄德 | 95 |
| 6 | 孙权 | 83 |
| 7 | 宋公明 | 75 |
+----+-----------+---------+
7 rows in set (0.00 sec)-- 表达式包含多个字段
mysql> select id,name,10+math+chinese+english from exam_result;
+----+-----------+-------------------------+
| id | name | 10+math+chinese+english |
+----+-----------+-------------------------+
| 1 | 唐三藏 | 231 |
| 2 | 孙悟空 | 252 |
| 3 | 猪悟能 | 286 |
| 4 | 曹孟德 | 243 |
| 5 | 刘玄德 | 195 |
| 6 | 孙权 | 231 |
| 7 | 宋公明 | 180 |
+----+-----------+-------------------------+
7 rows in set (0.00 sec)
④ 为查询结果指定别名 as
语法如下所示:
select 列字段 [as] alias_name [...] from 表名;
举个例子就懂了:
mysql> select id,name,math+chinese+english as 总分 from exam_result;
+----+-----------+--------+
| id | name | 总分 |
+----+-----------+--------+
| 1 | 唐三藏 | 221 |
| 2 | 孙悟空 | 242 |
| 3 | 猪悟能 | 276 |
| 4 | 曹孟德 | 233 |
| 5 | 刘玄德 | 185 |
| 6 | 孙权 | 221 |
| 7 | 宋公明 | 170 |
+----+-----------+--------+
7 rows in set (0.00 sec)
⑤ 结果去重 distinct
-- 去重前
mysql> select math+chinese+english as 总分 from exam_result;
+--------+
| 总分 |
+--------+
| 221 |
| 242 |
| 276 |
| 233 |
| 185 |
| 221 |
| 170 |
+--------+
7 rows in set (0.00 sec)-- 去重后
mysql> select distinct math+chinese+english as 总分 from exam_result;
+--------+
| 总分 |
+--------+
| 221 |
| 242 |
| 276 |
| 233 |
| 185 |
| 170 |
+--------+
6 rows in set (0.00 sec)
2、where 条件
这里的 where 条件其实就是对我们已经选择的列字段,进行某种条件筛选的策略!其实就相当于我们以前在学 c/c++ 的时候所学的 if 语句,所以肯定也有对应的比较、逻辑运算符供我们使用!
要注意的是,别名不能用在 where 条件中!
🎏 where 条件在 sql 语句中的执行顺序

为什么强调这个执行顺序呢❓❓❓
这是因为只有当我们理解了执行顺序之后,才会理解一些 mysql 的错误语句到底错在哪,或者是要做什么工作!
比如为什么 不能在 where 语句中使用别名,这是因为别名是在 select 部分使用的,是为了最后呈现出来表字段的别名。如果在 where 语句使用了 select 语句部分的别名,那么因为执行顺序问题,where 语句在 select 语句之前就执行了,肯定就找不到该别名去执行,就报错了!
比较运算符
| 运算符 | 说明 |
|---|---|
>、≥、<、≤ | 大于,大于等于,小于,小于等于 |
= | 等于,对于 NULL 不安全,例如 NULL = NULL 的结果是 NULL |
<=> | 等于,对于 NULL 安全,例如 NULL <=> NULL 的结果是 TRUE/1 |
!=、<> | 不等于 |
between a and b | 范围匹配为 [a, b],如果 a ≤ value ≤ b,返回 TRUE/1 |
in (option, ...) | 如果是 option 中的任意一个,返回 TRUE/1 |
is null | 判断是否为 null |
is not null | 判断是否不为 null |
like | 模糊匹配,% 表示任意多个(包括 0 个)任意字,_ 表示任意一个字符 |
逻辑运算符
| 运算符 | 说明 |
|---|---|
and | 多个条件必须都为 TRUE/1,结果才是 TRUE/1 |
or | 任意一个条件为 TRUE/1,结果为 TRUE/1 |
not | 条件为 TRUE/1,结果为 FALSE/0 |
使用案例
① 英语不及格的同学及英语成绩,即小于60分
-- 筛选前
mysql> select name,english as '英语' from exam_result;
+-----------+--------+
| name | 英语 |
+-----------+--------+
| 唐三藏 | 56 |
| 孙悟空 | 77 |
| 猪悟能 | 90 |
| 曹孟德 | 67 |
| 刘玄德 | 45 |
| 孙权 | 78 |
| 宋公明 | 30 |
+-----------+--------+
7 rows in set (0.00 sec)-- 筛选后
mysql> select name,english as '英语' from exam_result where english < 60;
+-----------+--------+
| name | 英语 |
+-----------+--------+
| 唐三藏 | 56 |
| 刘玄德 | 45 |
| 宋公明 | 30 |
+-----------+--------+
3 rows in set (0.00 sec)
② 语文成绩在 [80, 90] 分的同学及语文成绩
除了下面的 between and 之外,还可以使用 and,但是太麻烦了,这里就不演示了!
-- 筛选前
mysql> select name,chinese as '语文' from exam_result;
+-----------+--------+
| name | 语文 |
+-----------+--------+
| 唐三藏 | 67 |
| 孙悟空 | 87 |
| 猪悟能 | 88 |
| 曹孟德 | 82 |
| 刘玄德 | 55 |
| 孙权 | 70 |
| 宋公明 | 75 |
+-----------+--------+
7 rows in set (0.00 sec)-- 使用between and筛选
mysql> select name,chinese as '语文' from exam_result where chinese between 80 and 90;
+-----------+--------+
| name | 语文 |
+-----------+--------+
| 孙悟空 | 87 |
| 猪悟能 | 88 |
| 曹孟德 | 82 |
+-----------+--------+
3 rows in set (0.00 sec)
③ 数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩
-- 筛选前
mysql> select name,math as '数学' from exam_result;
+-----------+--------+
| name | 数学 |
+-----------+--------+
| 唐三藏 | 98 |
| 孙悟空 | 78 |
| 猪悟能 | 98 |
| 曹孟德 | 84 |
| 刘玄德 | 85 |
| 孙权 | 73 |
| 宋公明 | 65 |
+-----------+--------+
7 rows in set (0.00 sec)-- 使用or筛选
mysql> select name,math as '数学' from exam_result where math=58 or math=59 or math=98 or math=99;
+-----------+--------+
| name | 数学 |
+-----------+--------+
| 唐三藏 | 98 |
| 猪悟能 | 98 |
+-----------+--------+
2 rows in set (0.00 sec)
还可用 in 进行筛选,更加的优雅:
-- 使用in筛选,更加的优雅!
mysql> select name,math as '数学' from exam_result where math in(58, 59, 98, 99);
+-----------+--------+
| name | 数学 |
+-----------+--------+
| 唐三藏 | 98 |
| 猪悟能 | 98 |
+-----------+--------+
2 rows in set (0.00 sec)
④ 姓孙的同学及孙某同学
解释一下这个要求,姓孙的同学他们的名字是可以不定长的,但是孙某同学,就是固定名长就是一个,是这个意思!
像这种模糊的需求,我们就可以用模糊匹配 like 来解决!
-- 筛选前
mysql> select name from exam_result;
+-----------+
| name |
+-----------+
| 唐三藏 |
| 孙悟空 |
| 猪悟能 |
| 曹孟德 |
| 刘玄德 |
| 孙权 |
| 宋公明 |
+-----------+
7 rows in set (0.00 sec)-- 通过like以及两个通配符来筛选,两者用or连接
-- % 表示匹配任意多个(包括0个)任意字符
-- _ 表示匹配严格的一个任意字符
mysql> select name from exam_result where name like '孙%' or name like '孙_';
+-----------+
| name |
+-----------+
| 孙悟空 |
| 孙权 |
+-----------+
2 rows in set (0.00 sec)
⑤ 语文成绩好于英语成绩的同学
-- 筛选前
mysql> select name,chinese as '语文',math as '数学' from exam_result;
+-----------+--------+--------+
| name | 语文 | 数学 |
+-----------+--------+--------+
| 唐三藏 | 67 | 98 |
| 孙悟空 | 87 | 78 |
| 猪悟能 | 88 | 98 |
| 曹孟德 | 82 | 84 |
| 刘玄德 | 55 | 85 |
| 孙权 | 70 | 73 |
| 宋公明 | 75 | 65 |
+-----------+--------+--------+
7 rows in set (0.00 sec)-- 筛选后
mysql> select name,chinese as '语文',math as '数学' from exam_result where chinese > math;
+-----------+--------+--------+
| name | 语文 | 数学 |
+-----------+--------+--------+
| 孙悟空 | 87 | 78 |
| 宋公明 | 75 | 65 |
+-----------+--------+--------+
2 rows in set (0.00 sec)
⑥ 总分在 200 分以上的同学
这里需要注意的是,别名不能用在 where 条件中!具体原因是和语句的执行有关,上面讲过!
-- 筛选前
mysql> select name, chinese+math+english '总分' from exam_result;
+-----------+--------+
| name | 总分 |
+-----------+--------+
| 唐三藏 | 221 |
| 孙悟空 | 242 |
| 猪悟能 | 276 |
| 曹孟德 | 233 |
| 刘玄德 | 185 |
| 孙权 | 221 |
| 宋公明 | 170 |
+-----------+--------+
7 rows in set (0.00 sec)-- 筛选后
mysql> select name, chinese+math+english '总分' from exam_result where chinese+math+english > 200;
+-----------+--------+
| name | 总分 |
+-----------+--------+
| 唐三藏 | 221 |
| 孙悟空 | 242 |
| 猪悟能 | 276 |
| 曹孟德 | 233 |
| 孙权 | 221 |
+-----------+--------+
5 rows in set (0.00 sec)
⑦ 语文成绩 > 80 并且不姓孙的同学
-- 筛选前
mysql> select name, chinese from exam_result where chinese>80;
+-----------+---------+
| name | chinese |
+-----------+---------+
| 孙悟空 | 87 |
| 猪悟能 | 88 |
| 曹孟德 | 82 |
+-----------+---------+
3 rows in set (0.00 sec)-- 通过and和not配合达到筛选目的
mysql> select name, chinese from exam_result where chinese>80 and name not like '孙%';
+-----------+---------+
| name | chinese |
+-----------+---------+
| 猪悟能 | 88 |
| 曹孟德 | 82 |
+-----------+---------+
2 rows in set (0.00 sec)
⑧ 猪某同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80
mysql> select name,chinese,math,english,chinese+math+english '总分' from exam_result where name like '猪%' and chinese+math+english>200 and chineese<math and english>80;
+-----------+---------+------+---------+--------+
| name | chinese | math | english | 总分 |
+-----------+---------+------+---------+--------+
| 猪悟能 | 88 | 98 | 90 | 276 |
+-----------+---------+------+---------+--------+
1 row in set (0.00 sec)
⑨ NULL 的查询
-- 查询 students 表
+-----+-------+-----------+-------+
| id | sn | name | qq |
+-----+-------+-----------+-------+
| 100 | 10010 | 唐大师 | NULL |
| 101 | 10001 | 孙悟空 | 11111 |
| 103 | 20002 | 孙仲谋 | NULL |
| 104 | 20001 | 曹阿瞒 | NULL |
+-----+-------+-----------+-------+
4 rows in set (0.00 sec)-- 查询 qq 号已知的同学姓名
select name, qq from students where qq is not NULL;
+-----------+-------+
| name | qq |
+-----------+-------+
| 孙悟空 | 11111 |
+-----------+-------+
1 row in set (0.00 sec)
下面再列举一下 NULL 和 NULL 的比较,= 和 <=> 的区别:
select NULL = NULL, NULL = 1, NULL = 0;
+-------------+----------+----------+
| NULL = NULL | NULL = 1 | NULL = 0 |
+-------------+----------+----------+
| NULL | NULL | NULL |
+-------------+----------+----------+
1 row in set (0.00 sec)select NULL <=> NULL, NULL <=> 1, NULL <=> 0;
+---------------+------------+------------+
| NULL <=> NULL | NULL <=> 1 | NULL <=> 0 |
+---------------+------------+------------+
| 1 | 0 | 0 |
+---------------+------------+------------+
1 row in set (0.00 sec)
3、order by 结果排序
在 mysql 中,order by 子句用于 对查询结果进行排序。它可以按照一个或多个列的值进行升序或降序排序。
以下是 order by 子句的基本语法:
select 列名 from 表名 order by 列名 [asc|desc];
其中,列名是要排序的列的名称,表名是要查询的表的名称。ASC(ascending) 表示按升序排序(默认),DESC(descending) 表示按降序排序。
如果要按多个列进行排序,可以在 order by 子句中指定多个列名,并用逗号分隔它们。查询结果将首先按第一个列进行排序,然后按第二个列进行排序,以此类推。
注意事项:
- 没有
order by子句的查询,返回的顺序是未定义的,永远不要依赖原来的插入表的这个顺序!- 多字段排序,排序优先级随书写顺序!(可以结合下面的案例③)
order by子句中是 可以使用列别名 的!(这个和子句的执行顺序有关系!)order by子句必须放在where条件后面使用!
🎏order by子句在 sql 语句中的执行顺序

从上图可以清晰看到执行的顺序,最重要的是第三步也就是 select 子句,它虽然是进行筛选和显示的执行,但是其实它这两个步骤是分开的,当加入了 order by 子句之后,select 子句会先进行筛选,目的是筛选出符合条件的数据集,然后 再交给第四步也就是 order by 子句进行排序,最后再回到 select 子句中进行最后的显示!
这也是为什么 order by 子句可以使用 select 子句中的别名的原因!
使用案例
① 同学及数学成绩,按数学成绩升序显示
mysql> select name,math from exam_result order by math asc; #升序
+-----------+------+
| name | math |
+-----------+------+
| 宋公明 | 65 |
| 孙权 | 73 |
| 孙悟空 | 78 |
| 曹孟德 | 84 |
| 刘玄德 | 85 |
| 唐三藏 | 98 |
| 猪悟能 | 98 |
+-----------+------+
7 rows in set (0.00 sec)mysql> select name,math from exam_result order by math desc; #降序
+-----------+------+
| name | math |
+-----------+------+
| 唐三藏 | 98 |
| 猪悟能 | 98 |
| 刘玄德 | 85 |
| 曹孟德 | 84 |
| 孙悟空 | 78 |
| 孙权 | 73 |
| 宋公明 | 65 |
+-----------+------+
7 rows in set (0.00 sec)
② 同学及 qq 号,按 qq 号排序显示
-- NULL 视为比任何值都小,升序出现在最上面
select name, qq from students order by qq;
+-----------+-------+
| name | qq |
+-----------+-------+
| 唐大师 | NULL |
| 孙仲谋 | NULL |
| 曹阿瞒 | NULL |
| 孙悟空 | 11111|
+-----------+-------+
4 rows in set (0.00 sec)-- NULL 视为比任何值都小,降序出现在最下面
select name, qq from students order by qq desc;
+-----------+-------+
| name | qq |
+-----------+-------+
| 孙悟空 | 11111 |
| 唐大师 | NULL |
| 孙仲谋 | NULL |
| 曹阿瞒 | NULL |
+-----------+-------+
4 rows in set (0.00 sec)
③ 查询同学各门成绩,依次按 数学降序,英语升序,语文升序的方式显示
注意,多字段排序,排序优先级随书写顺序!所以如果 math 高的话,就算 english 低了也会排在前面!
mysql> select name,math,english,chinese from exam_result order by math desc,english asc,chinese asc;
+-----------+------+---------+---------+
| name | math | english | chinese |
+-----------+------+---------+---------+
| 唐三藏 | 98 | 56 | 67 |
| 猪悟能 | 98 | 90 | 88 |
| 刘玄德 | 85 | 45 | 55 |
| 曹孟德 | 84 | 67 | 82 |
| 孙悟空 | 78 | 77 | 87 |
| 孙权 | 73 | 78 | 70 |
| 宋公明 | 65 | 30 | 75 |
+-----------+------+---------+---------+
7 rows in set (0.00 sec)
④ 查询同学及总分,由高到低
说明 order by 中也可以使用表达式!
mysql> select name,chinese+math+english as '总分' from exam_result order by chinese+math+english desc;
+-----------+--------+
| name | 总分 |
+-----------+--------+
| 猪悟能 | 276 |
| 孙悟空 | 242 |
| 曹孟德 | 233 |
| 唐三藏 | 221 |
| 孙权 | 221 |
| 刘玄德 | 185 |
| 宋公明 | 170 |
+-----------+--------+
7 rows in set (0.00 sec)
除此之外,order by 子句中是可以使用列别名的:
mysql> select name,chinese+math+english as '总分' from exam_result order by '总分' desc;
+-----------+--------+
| name | 总分 |
+-----------+--------+
| 唐三藏 | 221 |
| 孙悟空 | 242 |
| 猪悟能 | 276 |
| 曹孟德 | 233 |
| 刘玄德 | 185 |
| 孙权 | 221 |
| 宋公明 | 170 |
+-----------+--------+
7 rows in set (0.00 sec)
⑤ 查询姓孙的同学或者姓曹的同学数学成绩,结果按数学成绩由高到低显示
从下面的操作可以看出 order by 子句要放在 where 条件的后面!
mysql> select name,math from exam_result where name like '孙%' or name like '曹%' order by math desc;
+-----------+------+
| name | math |
+-----------+------+
| 曹孟德 | 84 |
| 孙悟空 | 78 |
| 孙权 | 73 |
+-----------+------+
3 rows in set (0.00 sec)-- order by子句要放在where条件的后面!
mysql> select name,math from exam_result order by math desc where name like '孙%' or name like '曹%';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where name like '孙%' or name like '曹%'' at line 1
4、limit 筛选分页结果
limit 是一个用于限制查询结果返回的子句。它可以用于 指定从查询结果中返回的行数,也可以用于指定返回结果的偏移量。
一般语法如下:
# 从 0 开始,筛选 n 条结果
select 列名 from 表名 [where ...] [order by ...] limit n;# 从 s 开始,筛选 n 条结果
select 列名 from 表名 [where ...] [order by ...] limit s, n;# 从 s 开始,筛选 n 条结果,比第二种用法更明确,更建议使用!
select 列名 from 表名 [where ...] [order by ...] limit n offset s;
建议:对未知表进行查询时,最好加一条 limit 子句,避免因为表中数据过大,查询全表数据导致数据库卡死!
举个例子,按 id 和 name 进行分页,每页 3 条记录,分别显示 第 1、2、3 页(这里使用上述的第三种方式):
mysql> select id,name from exam_result limit 3 offset 0;
+----+-----------+
| id | name |
+----+-----------+
| 1 | 唐三藏 |
| 2 | 孙悟空 |
| 3 | 猪悟能 |
+----+-----------+
3 rows in set (0.00 sec)mysql> select id,name from exam_result limit 3 offset 3;
+----+-----------+
| id | name |
+----+-----------+
| 4 | 曹孟德 |
| 5 | 刘玄德 |
| 6 | 孙权 |
+----+-----------+
3 rows in set (0.00 sec)mysql> select id,name from exam_result limit 3 offset 6;
+----+-----------+
| id | name |
+----+-----------+
| 7 | 宋公明 |
+----+-----------+
1 row in set (0.00 sec)
🎏 limit 子句在 sql 语句中的执行顺序
很明显,因为 limit 子句是用来限制 ”显示“ 结果的,那么就是在比较靠后的步骤中才执行的!

Ⅲ. 更新数据
1、update语句
在 mysql 中,update 语句用于更新表中的数据。它的基本语法如下:
update 表名 set column1=value1 [, column2=value2, ...] [where 条件] [order by ...] [limit ...];
注意,如果没有指定 where 子句,update 语句将会更新表中的所有行。因此,在使用 update 语句时,请确保提供正确的条件,以避免意外更新整个表的数据。
2、使用案例
① 将孙悟空同学的数学成绩变更为 80 分
-- 查看原数据
mysql> select name,math from exam_result where name='孙悟空';
+-----------+------+
| name | math |
+-----------+------+
| 孙悟空 | 78 |
+-----------+------+
1 row in set (0.00 sec)-- 数据更新
mysql> update exam_result set math=80 where name='孙悟空';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0-- 查看更新后数据
mysql> select name,math from exam_result where name='孙悟空';
+-----------+------+
| name | math |
+-----------+------+
| 孙悟空 | 80 |
+-----------+------+
1 row in set (0.00 sec)
② 将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分
-- 一次更新多个列-- 查看原数据
mysql> select name,math,chinese from exam_result where name='曹孟德';
+-----------+------+---------+
| name | math | chinese |
+-----------+------+---------+
| 曹孟德 | 84 | 82 |
+-----------+------+---------+
1 row in set (0.00 sec)-- 数据更新
mysql> update exam_result set math=60,chinese=70 where name='曹孟德';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0-- 查看更新后数据
mysql> select name,math,chinese from exam_result where name='曹孟德';
+-----------+------+---------+
| name | math | chinese |
+-----------+------+---------+
| 曹孟德 | 60 | 70 |
+-----------+------+---------+
1 row in set (0.00 sec)
③ 将总成绩倒数前三的 3 位同学的数学成绩加上 30 分
-- 更新值为原值基础上变更-- 查看原数据
mysql> select name,math+chinese+english total from exam_result order by total asc limit 3;
+-----------+-------+
| name | total |
+-----------+-------+
| 宋公明 | 170 |
| 刘玄德 | 185 |
| 曹孟德 | 197 |
+-----------+-------+
3 rows in set (0.00 sec)-- 数据更新,注意mysql不支持math += 30这种语法
mysql> update exam_result set math=math+30 order by math+chinese+english limit 3;
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0-- 按总成绩排序后查询结果
mysql> select name,math+chinese+english total from exam_result order by total asc limit 3;
+-----------+-------+
| name | total |
+-----------+-------+
| 宋公明 | 200 |
| 刘玄德 | 215 |
| 唐三藏 | 221 |
+-----------+-------+
3 rows in set (0.00 sec)
④ 将所有同学的语文成绩更新为原来的 2 倍
注意:更新全表的语句慎用!
-- 没有 WHERE 子句,则更新全表-- 查看原数据
mysql> select name,chinese from exam_result;
+-----------+---------+
| name | chinese |
+-----------+---------+
| 唐三藏 | 67 |
| 孙悟空 | 87 |
| 猪悟能 | 88 |
| 曹孟德 | 70 |
| 刘玄德 | 55 |
| 孙权 | 70 |
| 宋公明 | 75 |
+-----------+---------+
7 rows in set (0.00 sec)-- 数据更新
mysql> update exam_result set chinese=chinese*2;
Query OK, 7 rows affected (0.00 sec)
Rows matched: 7 Changed: 7 Warnings: 0-- 查看更新后数据
mysql> select name,chinese from exam_result;
+-----------+---------+
| name | chinese |
+-----------+---------+
| 唐三藏 | 134 |
| 孙悟空 | 174 |
| 猪悟能 | 176 |
| 曹孟德 | 140 |
| 刘玄德 | 110 |
| 孙权 | 140 |
| 宋公明 | 150 |
+-----------+---------+
7 rows in set (0.00 sec)
Ⅳ. 删除数据
1、delete删除数据
delete 语句用于从表中删除满足指定条件的行,可以根据需要删除部分或全部数据。delete 语句是一种 DML(数据操作语言)操作,它 会生成事务日志,并且可以回滚。
delete 语句的语法如下:
delete from 表名 [where 条件] [order by ...] [limit ...];
另外注意的是,这里说的删除操作,都是针对表中的数据,而不是删除表的操作的!
① 删除孙悟空同学的考试成绩
-- 查看原数据
mysql> select * from exam_result where name='孙悟空';
+----+-----------+---------+------+---------+
| id | name | chinese | math | english |
+----+-----------+---------+------+---------+
| 2 | 孙悟空 | 174 | 80 | 77 |
+----+-----------+---------+------+---------+
1 row in set (0.00 sec)-- 删除数据
mysql> delete from exam_result where name='孙悟空';
Query OK, 1 row affected (0.00 sec)-- 查看删除结果
mysql> select * from exam_result where name='孙悟空';
Empty set (0.00 sec)
② 删除整张表数据
注意,删除整表操作要慎用!
-- 准备测试表
create table for_delete (id int primary key auto_increment,name varchar(20));-- 插入测试数据
insert into for_delete (name) values ('A'), ('B'), ('C');-- 查看测试数据
mysql> select * from for_delete;
+----+------+
| id | name |
+----+------+
| 1 | A |
| 2 | B |
| 3 | C |
+----+------+
3 rows in set (0.00 sec)
-- 删除整表数据
mysql> delete from for_delete;
Query OK, 3 rows affected (0.00 sec)-- 查看删除结果
mysql> select * from for_delete;
Empty set (0.00 sec)
-- 再插入一条数据,自增 id 在原值上增长
mysql> insert into for_delete (name) values('liren');
Query OK, 1 row affected (0.01 sec)-- 查看数据
mysql> select * from for_delete;
+----+-------+
| id | name |
+----+-------+
| 4 | liren |
+----+-------+
1 row in set (0.00 sec)-- 查看表结构,会有 AUTO_INCREMENT=n 项,依然是不变的!这和下面的截断表不太一样!
mysql> show create table for_delete\G;
*************************** 1. row ***************************Table: for_delete
Create Table: CREATE TABLE `for_delete` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
2、truncate截断表
truncate 语句用于 删除表中的所有数据,它会将表重置为空表,但保留表的结构。truncate 语句是一种 DDL(数据定义语言)操作,它 不会生成事务日志,并且不能回滚。
truncate 语句的语法如下:
truncate table 表名;
使用说明:
- 只能对整表操作,不能像
delete一样针对部分数据操作;- 实际上
truncate不对数据操作,所以比delete更快,但是truncate在删除数据的时候,并不经过真正的事务,所以无法回滚!- 会重置
auto_increment项- 这个语句慎用!
操作和 delete 基本是一样的,只不过没有 where 等子句罢了,只能对整个表进行操作!
下面我们同样做个例子,看看它们的区别:
① 删除整张表数据
-- 准备测试表
create table for_truncate (id int primary key auto_increment,name varchar(20));
Query OK, 0 rows affected (0.02 sec)-- 插入测试数据
insert into for_delete (name) values ('A'), ('B'), ('C');-- 查看测试数据
mysql> select * from for_truncate;
+----+------+
| id | name |
+----+------+
| 1 | A |
| 2 | B |
| 3 | C |
+----+------+
3 rows in set (0.00 sec)
-- 截断整表数据,注意影响行数是 0,所以实际上没有对数据真正操作
mysql> truncate table for_truncate;
Query OK, 0 rows affected (0.01 sec)-- 查看删除结果
mysql> select * from for_truncate;
Empty set (0.00 sec)
-- 再插入一条数据,自增 id 在重新增长
mysql> insert into for_truncate (name) values('liren');
Query OK, 1 row affected (0.01 sec)-- 查看数据
mysql> select * from for_truncate;
+----+-------+
| id | name |
+----+-------+
| 1 | liren |
+----+-------+
1 row in set (0.00 sec)-- 查看表结构,会有 AUTO_INCREMENT=2 项,这是因为我们重新插入了一条数据后的,说明auto_increment被重新设置了
mysql> show create table for_truncate \G;
*************************** 1. row ***************************Table: for_truncate
Create Table: CREATE TABLE `for_truncate` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
1 row in set (0.01 sec)
3、两者的区别
下面是 delete 和 truncate 之间的一些主要区别:
delete语句是逐行删除数据,而truncate语句是一次性删除表中所有数据。delete语句可以使用where子句来指定删除的条件,而truncate语句不支持where子句。delete语句会 生成事务日志,并且可以回滚,而truncate语句 不会生成事务日志,并且不能回滚。delete语句在删除每一行时都会触发相应的触发器(如果有定义的话),而truncate语句不会触发触发器。delete语句的 执行速度相对较慢,特别是在删除大量数据时,而truncate语句的 执行速度相对较快,因为它是一次性删除所有数据。
根据具体的需求和场景,选择使用 delete 还是 truncate 取决于你想要删除的数据量、是否需要回滚以及是否需要触发触发器等因素。
Ⅴ. 插入查询结果
1、语法
其实我们可以通过 insert 子句和 select 子句的配合,达到插入一些需要的查询结果的目的!
其语法如下所示:
insert into 表名 [(列名 [, 列名 ...])] select ...
这语法无非就是将 insert 子句后面 values() 部分替换成 select 子句罢了!
2、案例
下面给出一个案例:要求删除表中的重复记录,让重复的数据只能有一份。
这里的思路是这样子的:一般我们很少直接对原表中的数据进行操作,而是先创建一个新表,其表结构和原表的结构是一模一样的。然后通过 insert+select 语句将去重的结果进行插入到新表,最后将原表使用 rename table 语句重命名为旧表当作备份,而新表重命名为原表使用!
可能有人会问,为什么最后是通过
rename table语句重命名的方式来操作❓❓❓
rename table命令在mysql中是一个 原子操作,它会自动处理表的元数据和相关的索引、触发器等信息,它还会确保在重命名过程中不会丢失任何数据。 举个例子,假设我们今天在
linux中要上传一个文件到指定目录中,并且要求是原子操作,而因为这个文件的大小很大,所以上传速度很慢,此时其它在该目录下的业务也就自然被阻塞了。 为了解决这个问题,我们可以先将这个文件,上传到一个临时目录中,然后等上传结束之后,通过
mv指令直接更改目录名为指定的目录名,这个操作也是原子操作,符合要求,并且一步到位,不会干扰到其它业务的处理! 所以使用重命名的方式,其实就是 单纯地想等一切当作都就绪,然后再统一放入、更新、生效等!这对于
rename table来说也是如此!
预处理:
-- 创建原数据表
CREATE TABLE duplicate_table (id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)-- 插入测试数据
INSERT INTO duplicate_table VALUES(100, 'aaa'),(100, 'aaa'),(200, 'bbb'),(200, 'bbb'),(200, 'bbb'),(300, 'ccc');
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
操作思路:
-- 创建一张空表 no_duplicate_table,结构和 duplicate_table 一样
mysql> create table no_duplicate_table like duplicate_table;
Query OK, 0 rows affected (0.00 sec)-- 将 duplicate_table 的去重数据插入到 no_duplicate_table
mysql> insert into no_duplicate_table select distinct * from duplicate_table;
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0-- 通过重命名表,实现原子的去重操作
mysql> rename table duplicate_table to old_duplicate_table, no_duplicate_table to duplicate_table;
Query OK, 0 rows affected (0.02 sec)-- 查看最终结果
mysql> select * from duplicate_table;
+------+------+
| id | name |
+------+------+
| 100 | aaa |
| 200 | bbb |
| 300 | ccc |
+------+------+
3 rows in set (0.00 sec)mysql> select * from old_duplicate_table;
+------+------+
| id | name |
+------+------+
| 100 | aaa |
| 100 | aaa |
| 200 | bbb |
| 200 | bbb |
| 200 | bbb |
| 300 | ccc |
+------+------+
6 rows in set (0.00 sec)
Ⅵ. 聚合函数
1、常见聚合函数
这些常见的聚合函数可以与 select 语句一起使用,用于对数据进行汇总和统计操作:
| 函数 | 声明 |
|---|---|
count( [distinct] 列名 ) | 用于计算指定列或表中的行数 |
sum( [distinct] 列名 ) | 用于计算指定列或表中数值列的总和(不是数字没有意义) |
avg( [distinct] 列名 ) | 用于计算指定列或表中数值列的平均值(不是数字没有意义) |
max( [distinct] 列名 ) | 用于找出指定列或表中数值列的最大值(不是数字没有意义) |
min( [distinct] 列名 ) | 用于找出指定列或表中数值列的最小值(不是数字没有意义) |
group_concat( 列名 分隔符 ) | 用于将指定列的值连接成一个字符串,并用指定的分隔符分隔 |
注意,在使用聚合函数的时候,如果后面没有跟着 group by 指定的列字段的话,那么 select 语句是除了聚会函数以外,不能列举其它无关的列字段!
2、案例
① 统计班级共有多少同学
-- 最好使用 * 做统计,不受 NULL 影响mysql> select count(*) from exam_result;
+----------+
| count(*) |
+----------+
| 7 |
+----------+
1 row in set (0.00 sec)
② 统计班级收集的数学成绩有多少
-- NULL 不会计入结果mysql> select count(math) from exam_result;
+-------------+
| count(math) |
+-------------+
| 7 |
+-------------+
1 row in set (0.00 sec)
③ 统计本次考试的数学成绩分数去重后的个数
-- COUNT(DISTINCT math) 统计的是去重成绩数量
mysql> select count(distinct math) from exam_result;
+----------------------+
| count(distinct math) |
+----------------------+
| 6 |
+----------------------+
1 row in set (0.01 sec)-- 可以使用别名
mysql> select count(distinct math) 数学 from exam_result;
+--------+
| 数学 |
+--------+
| 6 |
+--------+
1 row in set (0.00 sec)
④ 统计数学成绩总分
mysql> select sum(math) 数学总分 from exam_result;
+--------------+
| 数学总分 |
+--------------+
| 581 |
+--------------+
1 row in set (0.00 sec)
⑤ 统计平均总分
mysql> select avg(chinese+math+english) 平均总分 from exam_result;
+--------------------+
| 平均总分 |
+--------------------+
| 221.14285714285714 |
+--------------------+
1 row in set (0.00 sec)
⑥ 返回英语最高分
mysql> select max(english) 英语 from exam_result;
+--------+
| 英语 |
+--------+
| 90 |
+--------+
1 row in set (0.00 sec)-- 注意不能select无关的列字段
mysql> select name, max(english) 英语 from exam_result;
ERROR 1140 (42000): In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'testdb.exam_result.name'; this is incompatible with sql_mode=only_full_group_by
⑦ 返回 > 70 分以上的数学最低分
mysql> select min(math) 数学 from exam_result where math>70;
+--------+
| 数学 |
+--------+
| 73 |
+--------+
1 row in set (0.00 sec)
Ⅶ. group by分组查询 && having 结果过滤
1、group by语法
在 mysql 中,group by 子句用于 将结果集按照指定列进行分组。它通常与聚合函数(如 SUM、COUNT、AVG 等)一起使用,以便对每个组应用聚合函数并返回结果。
其语法如下:
select 列名1, 列名2, ... 列名n from 表名 [where 条件] group by 列名1, 列名2, ... 列名n;
在这个语法中,列名1,列名2,… 列名n 是想要按照其进行分组的列。我们可以指定一个或多个列作为分组依据,而 where 子句用于筛选出符合条件的行。
注意事项:
group by子句的 执行顺序是在where子句之后,在select子句之前的。- 只要使用了
group by子句,那么除了在group by中指定的列字段,以及聚合函数之外,其它列字段一般不能出现在select子句中。
案例准备工作
- 准备工作,创建一个雇员信息表(来自oracle 9i的经典测试表)
- EMP员工表
- DEPT部门表
- SALGRADE工资等级表
这里提前准备好了一个 sql 文件 scott_data.sql,它已经为我们做好了数据库的创建、表的创建和一些数据的插入:

我们首先要理解一个点,就是
group by子句是在聚合函数也就是select子句之前执行的,为什么呢❓❓❓ 一般来说,我们使用
group by子句的就是为了将同一个列字段,再进行细分来进行分组统计。也就是说 分组的目的就是为了分组之后,更方便进行聚合统计!
下面我们举个例子,显示每个部门的平均工资和最高工资。
我们先简单的打印出所有部门的平均工资和最高工资:
mysql> select avg(sal) 平均工资, max(sal) 最高工资 from emp; +--------------+--------------+ | 平均工资 | 最高工资 | +--------------+--------------+ | 2073.214286 | 5000.00 | +--------------+--------------+ 1 row in set (0.00 sec) 我们再试试看打印出分组后的部门情况:
-- 分组前 mysql> select deptno 部门 from emp; +--------+ | 部门 | +--------+ | 20 | | 30 | | 30 | | 20 | | 30 | | 30 | | 10 | | 20 | | 10 | | 30 | | 20 | | 30 | | 20 | | 10 | +--------+ 14 rows in set (0.00 sec)-- 分组后 mysql> select deptno 部门 from emp group by deptno; +--------+ | 部门 | +--------+ | 10 | | 20 | | 30 | +--------+ 3 rows in set (0.00 sec) 此时我们再将两者结合:
mysql> select deptno 部门,avg(sal) 平均工资,max(sal) 最高工资 from emp group by deptno; +--------+--------------+--------------+ | 部门 | 平均工资 | 最高工资 | +--------+--------------+--------------+ | 10 | 2916.666667 | 5000.00 | | 20 | 2175.000000 | 3000.00 | | 30 | 1566.666667 | 2850.00 | +--------+--------------+--------------+ 3 rows in set (0.00 sec) 它就将每个部门的平均工资和最高工资查询出来了!
对于上述的操作,其实含义就是先将部门的列字段进行分组,分成不同的部门,即
group by deptno;此时只有当我们将这个分组好的几个部门交给select子句中的聚合函数去统计,其统计出来的才是各个部门的数据! 其实可以想象 在逻辑上,分组操作其实就是将一张表,拆成了多张子表,然后再分别对各自的子表进行聚合统计!

反过来,如果说我们先将所有员工的平均工资都求出来,再进行分组,此时平均工资不就又乱套了,就得重新再求一遍分组中的平均工资等数据了,对不对,所以
mysql没有这么笨,直接 让group by子句在select子句聚合统计前执行即可避免这个情况!
2、having语法
在 mysql 中,having 子句 用于在 group by 子句之后对结果进行过滤。它允许使用聚合函数对分组后的数据进行条件过滤。
其语法如下:
select 列名 from 表名 [where 条件] group by 列名 having 条件
🎏注意事项:
having子句中 可以使用select子句中的别名!having子句中的条件 可以使用聚合函数(如SUM、COUNT、AVG等)对分组后的数据进行过滤,只有满足having条件的分组才会被包含在结果中。having子句 只能在group by子句之后使用,并且用于过滤分组后的结果。如果 只需要对具体的任意列进行过滤,而不是对分组后的结果进行过滤,应该使用where子句。
以下是一个示例,假设我们有一个名为 orders 的表,其中包含订单信息,包括订单号、客户ID和订单总金额。我们想要找到订单总金额大于 1000 的客户ID。
select customer_id sum(order_count) total from orders group by customer_id having total>1000;
3、案例
① 显示每个部门的每种岗位的平均工资和最低工资
其实相当于在上面的案例中,将部门分为不同的小组之后,将这些小组再次细分为不同的工作:
mysql> select deptno 部门,job 岗位,avg(sal) 平均工资,max(sal) 最高工资 from emp group by deptno,job;
+--------+-----------+--------------+--------------+
| 部门 | 岗位 | 平均工资 | 最高工资 |
+--------+-----------+--------------+--------------+
| 10 | CLERK | 1300.000000 | 1300.00 |
| 10 | MANAGER | 2450.000000 | 2450.00 |
| 10 | PRESIDENT | 5000.000000 | 5000.00 |
| 20 | ANALYST | 3000.000000 | 3000.00 |
| 20 | CLERK | 950.000000 | 1100.00 |
| 20 | MANAGER | 2975.000000 | 2975.00 |
| 30 | CLERK | 950.000000 | 950.00 |
| 30 | MANAGER | 2850.000000 | 2850.00 |
| 30 | SALESMAN | 1400.000000 | 1600.00 |
+--------+-----------+--------------+--------------+
9 rows in set (0.01 sec)
② 显示平均工资低于2000的部门和它的平均工资
having 经常和 group by 搭配使用,作用是对分组进行筛选,作用有些像 where,但是原理和 where 其实是不一样的!
mysql> select deptno 部门,avg(sal) 平均工资 from emp group by deptno having 平均工资<2000;
+--------+--------------+
| 部门 | 平均工资 |
+--------+--------------+
| 30 | 1566.666667 |
+--------+--------------+
1 row in set (0.00 sec)
下面顺便来看一下它和 where 子句的区别:
mysql> select deptno 部门,avg(sal) 平均工资 from emp where sal<2000 group by deptno;
+--------+--------------+
| 部门 | 平均工资 |
+--------+--------------+
| 10 | 1300.000000 |
| 20 | 950.000000 |
| 30 | 1310.000000 |
+--------+--------------+
3 rows in set (0.00 sec)
这是什么情况,为什么用 where 子句出来的有三个结果,而且其中部门一样的平均工资也不同呀❓❓
还记得我们上面注意事项中提到的 having 子句和 where 子句它们的执行顺序是不同的吗,where 语句是在 from 之后也就是选表之后执行的筛选,此时筛选出来的是整个 sal 字段中少于 2000 的那些工资,最后再拿这些少于 2000 的去分组聚合统计,最后得到该结果。
而 having 则是在分组聚合之后才拿到的数据,也就是之前整个 sal 字段的工资根据分组后聚合统计后,得到的结果,然后根据该结果再去筛选出来的最终结果,这无疑是不一样的操作,导致了不一样的结果,要区分开!

实战OJ题
- 批量插入数据
- 找出所有员工当前薪水salary情况
- 查找最晚入职员工的所有信息
- 查找入职员工时间排名倒数第三的员工所有信息
- 查找薪水记录超过15条的员工号emp_no以及其对应的记录次数t
- 查找重复的电子邮箱
- Nth Highest Salary
