
一般来说,数据库的基本操作叫做 CRUD,是一种常见的编程术语,它代表了四个基本的数据库操作:创建(Create)、读取(Read)、更新(Update)和删除(Delete)。这些操作用于对数据库中的数据进行管理。
上面说的四种基本操作,是对四种操作的总览,具体使用的语句细分下去是有差别的,下面我们分别来学习它们!
SQL查询中各个关键字的执行先后顺序: from > on> join > where > group by > with > having > select> distinct > order by > limit。
insert语句insert [into] table_name
[(列名, 列名, ...)]
values (value_list) [, (value_list), ...]
其中:
value_list: value, [, value, ...] column 表示列字段value 表示对应各自 column 要填入的值column 和 value 一定要一一对应 根据 column 和 value_list 的个数不同,可以划分为下面的情况:

由于 主键 或者 唯一键 对应的值已经存在而导致插入失败:
-- 主键冲突
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:表中有冲突数据,并且数据已经被更新。replace 其实这和上面的操作是一样的,只不过有了 replace 这个语句专门来解决这种下面的情况:
其语法如下所示:
replace into 表名 (要插入的列字段) values (列字段对应的值); 举个例子:

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)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 |
-- 筛选前
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) 除了下面的 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)-- 筛选前
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) 这里需要注意的是,别名不能用在 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)-- 筛选前
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)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)-- 查询 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)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)-- 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 1limit 筛选分页结果 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 子句是用来限制 ”显示“ 结果的,那么就是在比较靠后的步骤中才执行的!

update语句 在 mysql 中,update 语句用于更新表中的数据。它的基本语法如下:
update 表名 set column1=value1 [, column2=value2, ...] [where 条件] [order by ...] [limit ...]; 注意,如果没有指定 where 子句,update 语句将会更新表中的所有行。因此,在使用 update 语句时,请确保提供正确的条件,以避免意外更新整个表的数据。
-- 查看原数据
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)-- 一次更新多个列
-- 查看原数据
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)-- 更新值为原值基础上变更
-- 查看原数据
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) 注意:更新全表的语句慎用!
-- 没有 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)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)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)下面是 delete 和 truncate 之间的一些主要区别:
delete 语句是逐行删除数据,而 truncate 语句是一次性删除表中所有数据。delete 语句可以使用 where 子句来指定删除的条件,而 truncate 语句不支持 where 子句。delete 语句会 生成事务日志,并且可以回滚,而 truncate 语句 不会生成事务日志,并且不能回滚。delete 语句在删除每一行时都会触发相应的触发器(如果有定义的话),而 truncate 语句不会触发触发器。delete 语句的 执行速度相对较慢,特别是在删除大量数据时,而 truncate 语句的 执行速度相对较快,因为它是一次性删除所有数据。根据具体的需求和场景,选择使用 delete 还是 truncate 取决于你想要删除的数据量、是否需要回滚以及是否需要触发触发器等因素。
其实我们可以通过 insert 子句和 select 子句的配合,达到插入一些需要的查询结果的目的!
其语法如下所示:
insert into 表名 [(列名 [, 列名 ...])] select ... 这语法无非就是将 insert 子句后面 values() 部分替换成 select 子句罢了!
下面给出一个案例:要求删除表中的重复记录,让重复的数据只能有一份。
这里的思路是这样子的:一般我们很少直接对原表中的数据进行操作,而是先创建一个新表,其表结构和原表的结构是一模一样的。然后通过 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) 这些常见的聚合函数可以与 select 语句一起使用,用于对数据进行汇总和统计操作:
函数 | 声明 |
|---|---|
count( [distinct] 列名 ) | 用于计算指定列或表中的行数 |
sum( [distinct] 列名 ) | 用于计算指定列或表中数值列的总和(不是数字没有意义) |
avg( [distinct] 列名 ) | 用于计算指定列或表中数值列的平均值(不是数字没有意义) |
max( [distinct] 列名 ) | 用于找出指定列或表中数值列的最大值(不是数字没有意义) |
min( [distinct] 列名 ) | 用于找出指定列或表中数值列的最小值(不是数字没有意义) |
group_concat( 列名 分隔符 ) | 用于将指定列的值连接成一个字符串,并用指定的分隔符分隔 |
注意,在使用聚合函数的时候,如果后面没有跟着 group by 指定的列字段的话,那么 select 语句是除了聚会函数以外,不能列举其它无关的列字段!
-- 最好使用 * 做统计,不受 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_bymysql> select min(math) 数学 from exam_result where math>70;
+--------+
| 数学 |
+--------+
| 73 |
+--------+
1 row in set (0.00 sec)group by分组查询 && having 结果过滤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 子句中。 这里提前准备好了一个 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子句聚合统计前执行即可避免这个情况!
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;
其实相当于在上面的案例中,将部门分为不同的小组之后,将这些小组再次细分为不同的工作:
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) 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 字段的工资根据分组后聚合统计后,得到的结果,然后根据该结果再去筛选出来的最终结果,这无疑是不一样的操作,导致了不一样的结果,要区分开!
