文章目录
- 前言
- Ⅰ. 创建新数据
- 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