目录
[一、数据更新:Update 的灵活用法](#一、数据更新:Update 的灵活用法)
[1. 单字段 / 多字段更新](#1. 单字段 / 多字段更新)
[2. 基于原数据的更新](#2. 基于原数据的更新)
[3. 全表更新(谨慎使用!)](#3. 全表更新(谨慎使用!))
[二、数据删除:Delete vs Truncate](#二、数据删除:Delete vs Truncate)
[1. Delete:删除部分 / 全表数据](#1. Delete:删除部分 / 全表数据)
[2. Truncate:截断表(重置表)](#2. Truncate:截断表(重置表))
[Delete vs Truncate 对比表](#Delete vs Truncate 对比表)
[1. Count:计数](#1. Count:计数)
[2. Sum/Avg:求和 / 平均值](#2. Sum/Avg:求和 / 平均值)
[3. Max/Min:最大 / 最小值](#3. Max/Min:最大 / 最小值)
[五、分组查询:Group By + Having](#五、分组查询:Group By + Having)
[1. 基础分组统计](#1. 基础分组统计)
[2. 多字段分组](#2. 多字段分组)
[3. Having 筛选分组结果](#3. Having 筛选分组结果)
[MySQL 语法速查表](#MySQL 语法速查表)
上一篇我们讲了 MySQL 的基础查询,今天继续深入 ------ 从数据更新(Update) 、数据删除(Delete/Truncate)到聚合函数 、分组查询(Group By),这些都是日常开发中高频使用的进阶操作。
一、数据更新:Update 的灵活用法
update用于修改表中已有的数据,核心语法是update 表名 set 列=值 [where 条件],下面看几个实用案例。
1. 单字段 / 多字段更新
(1)更新单个字段
比如把 "孙悟空" 的数学成绩改成 80 分:
sql
-- 先查原数据
select name, math from exam_result where name = '孙悟空';
-- 更新数据
update exam_result set math = 80 where name = '孙悟空';
-- 验证结果
select name, math from exam_result where name = '孙悟空';
(2)更新多个字段
同时修改 "曹孟德" 的数学和语文成绩:
sql
update exam_result set math = 60, chinese = 70 where name = '曹孟德';
2. 基于原数据的更新
如果要在原字段值基础上修改(比如 "给总分倒数前三的同学数学 + 30 分"),可以直接用字段做运算:
sql
-- 给总分倒数前三的同学数学+30分
update exam_result set math = math + 30
order by chinese + math + english limit 3;
注意:MySQL 不支持math += 30这种写法,必须显式写math = math + 30。
3. 全表更新(谨慎使用!)
如果省略where条件,会更新整个表的对应字段,比如把所有同学的语文成绩翻倍:
sql
-- 全表更新,风险高!
update exam_result set chinese = chinese * 2;
二、数据删除:Delete vs Truncate
删除数据有两种常用方式:delete和truncate,两者区别很大,一定要分清楚。
1. Delete:删除部分 / 全表数据
delete是DML 语句 ,可以删除部分数据(加where)或全表数据,支持事务回滚。
(1)删除指定数据
比如删除 "孙悟空" 的考试成绩:
sql
delete from exam_result where name = '孙悟空';
(2)删除全表数据
sql
-- 删除for_delete表的所有数据
delete from for_delete;
但delete删除全表后,自增主键不会重置(比如原主键到 3,删除后插入新数据主键从 4 开始)。
2. Truncate:截断表(重置表)
truncate是DDL 语句,只能删除全表数据,且不可回滚,但执行速度更快,会重置自增主键。
sql
-- 截断for_truncate表
truncate table for_truncate;
-- 插入新数据,主键从1开始
insert into for_truncate (name) values ('d');
Delete vs Truncate 对比表
| 特性 | Delete | Truncate |
|---|---|---|
| 操作类型 | DML(数据操作语言) | DDL(数据定义语言) |
| 能否删部分数据 | 可以(加 where) | 只能删全表 |
| 自增主键 | 不重置 | 重置为初始值 |
| 事务回滚 | 支持 | 不支持 |
| 执行速度 | 较慢(逐行删除) | 较快(直接重置表) |
三、插入查询结果:去重数据的小技巧
如果要把一张表的去重数据 插入另一张表,可以用insert into ... select ...语法,比如给重复数据去重:
sql
-- 1. 创建空表,结构和原表一致
create table no_duplicate_table like duplicate_table;
-- 2. 插入原表的去重数据
insert into no_duplicate_table select distinct * from duplicate_table;
-- 3. 重命名表,替换原表
rename table duplicate_table to old_table, no_duplicate_table to duplicate_table;
四、聚合函数:统计数据的利器
聚合函数用于对数据进行统计计算,常用的有count(计数)、sum(求和)、avg(平均值)、max(最大值)、min(最小值)。
1. Count:计数
count(*):统计所有行数(包括 null)count(字段):统计该字段非 null 的行数count(distinct 字段):统计该字段去重后的行数
sql
-- 统计学生总数
select count(*) from students;
-- 统计有QQ号的学生数
select count(qq) from students;
-- 统计数学成绩的去重数量
select count(distinct math) from exam_result;
2. Sum/Avg:求和 / 平均值
sql
-- 统计数学成绩总分
select sum(math) from exam_result;
-- 统计平均总分
select avg(chinese + math + english) as 平均总分 from exam_result;
3. Max/Min:最大 / 最小值
sql
-- 英语最高分
select max(english) from exam_result;
-- 70分以上的数学最低分
select min(math) from exam_result where math > 70;
五、分组查询:Group By + Having
group by用于按指定字段分组统计 ,常和聚合函数搭配;having用于对分组后的结果进行筛选(类似where,但where是分组前筛选)。
1. 基础分组统计
比如按部门统计平均工资和最高工资:
sql
select deptno, avg(sal), max(sal) from emp group by deptno;
2. 多字段分组
按 "部门 + 岗位" 分组,统计每个部门每种岗位的平均工资和最低工资:
sql
select deptno, job, avg(sal), min(sal) from emp group by deptno, job;
3. Having 筛选分组结果
筛选出 "平均工资低于 2000" 的部门:
sql
select deptno, avg(sal) as myavg
from emp
group by deptno
having myavg < 2000;
总结
今天讲的这些操作,是从 "单条数据操作" 到 "批量统计分析" 的进阶:
- Update:灵活修改数据,支持基于原字段运算;
- Delete/Truncate:删除数据的两种方式,注意自增主键和事务的区别;
- 聚合函数:快速统计数据(计数、求和、平均等);
- Group By+Having:分组统计并筛选结果。
MySQL 语法速查表
| 语法分类 | 核心关键字 / 语法结构 | 功能说明 | 示例代码(完整可执行) | 注意事项 |
|---|---|---|---|---|
| 数据更新 - Update | update 表名 set 列1=值1, 列2=值2... [where 条件] [order by ...] [limit ...]; |
修改表中已有数据,支持单字段 / 多字段、条件更新、基于原数据更新 | 1. 单字段更新:update exam_result set math=80 where name='孙悟空';2. 多字段更新:update exam_result set math=60, chinese=70 where name='曹孟德';3. 基于原数据更新:update exam_result set math=math+30 order by chinese+math+english limit 3; |
1. 无where条件会全表更新,风险极高;2. 不支持math+=30写法,需显式写math=math+30;3. 可搭配order by和limit实现精准批量更新 |
| 数据删除 - Delete | delete from 表名 [where 条件] [order by ...] [limit ...]; |
删除表中部分 / 全表数据,属于 DML 语句,支持事务回滚 | 1. 条件删除:delete from exam_result where name='孙悟空';2. 全表删除:delete from for_delete; |
1. 全表删除后自增主键不重置;2. 执行速度较慢(逐行删除);3. 可通过事务回滚恢复数据 |
| 数据删除 - Truncate | truncate table 表名; |
截断表(删除全表数据),属于 DDL 语句,不可回滚 | truncate table for_truncate; |
1. 仅支持全表删除,无法加where条件;2. 自增主键重置为初始值;3. 执行速度快,不可回滚 |
| 插入查询结果 | insert into 目标表 (列1,列2...) select 列1,列2... from 源表 [where 条件]; |
将源表的查询结果直接插入目标表,常用于数据去重、批量迁移 | 1. 去重插入:insert into no_duplicate_table select distinct * from duplicate_table;2. 条件插入:insert into student_bak (id,name) select id,name from students where qq is not null; |
1. 目标表需提前创建,列数和数据类型需与查询结果匹配;2. 可搭配distinct实现去重插入 |
| 聚合函数 - 计数 | count(*) / count(字段) / count(distinct 字段) |
统计数据行数,count(*)含 null,count(字段)不含 null,count(distinct 字段)去重统计 |
1. 统计总人数:select count(*) from students;2. 统计有 QQ 的人数:select count(qq) from students;3. 统计数学成绩去重数:select count(distinct math) from exam_result; |
count(1)与count(*)功能一致,性能相近,推荐使用count(*) |
| 聚合函数 - 求和 / 平均值 | sum(字段) / avg(字段) |
sum计算字段总和,avg计算字段平均值(自动忽略 null 值) |
1. 数学总分:select sum(math) from exam_result;2. 平均总分:select avg(chinese+math+english) as 平均总分 from exam_result; |
1. 仅支持数值类型字段;2. avg(字段)等价于sum(字段)/count(字段) |
| 聚合函数 - 最大 / 最小值 | max(字段) / min(字段) |
max获取字段最大值,min获取字段最小值(支持数值 / 字符串 / 日期类型) |
1. 英语最高分:select max(english) from exam_result;2. 70 分以上数学最低分:select min(math) from exam_result where math>70; |
字符串类型按字符编码排序(如max('a') < max('b')) |
| 分组查询 - Group By | select 分组字段, 聚合函数 from 表名 group by 分组字段1, 分组字段2...; |
按指定字段分组,对每组数据进行聚合统计 | 1. 单字段分组:select deptno, avg(sal), max(sal) from emp group by deptno;2. 多字段分组:select deptno, job, avg(sal), min(sal) from emp group by deptno, job; |
1. select后只能跟分组字段和聚合函数;2. 多字段分组时,先按第一个字段分组,再按第二个字段细分 |
| 分组筛选 - Having | select 分组字段, 聚合函数 from 表名 group by 分组字段 having 筛选条件; |
对分组后的结果进行筛选(分组后筛选,支持聚合函数别名) | select deptno, avg(sal) as myavg from emp group by deptno having myavg < 2000; |
1. where用于分组前筛选,having用于分组后筛选;2. having支持聚合函数别名,where不支持 |
注意
语法优先级:where(分组前)> group by(分组)> having(分组后)> order by(排序)> limit(分页);