【MySQL】CRUD基础详解
CRUD基础
- 前言:
- 数据库的层级结构
- 一、新增(Create)
- 1. 单行数据 + 全列插入
- 2. 单行数据的简写插入
- 3. 指定列插入
- 4. 多行数据插入
- 二、查询(Retrieve)
- 1. 全列查询
- 2. 指定列查询
- 3. 查询结果为表达式
- (1)不含字段的表达式
- (2)包含一个字段的表达式
- (3)包含多个字段的表达式
- 4. 别名
- 5. 去重(distinct)
- 6. 排序(order by)
- (1)排序的注意事项
- (2)NULL值的排序规则
- (3)使用表达式及别名排序
- (4)多字段排序
- 7. 条件查询(where)
- (1)比较运算符
- (2)逻辑运算符
- (3)条件查询的语法
- (4)条件查询示例
- 8. 分页查询(limit)
- (1)分页查询的语法
- (2)分页查询示例
- 三、修改(Update)
- 1. 修改操作的语法
- 2. 修改操作示例
- 四、删除(Delete)
- 1. 删除操作的语法
- 2. 删除操作示例
- 3. 生产环境中的删除策略
- 总结:
前言:
在MySQL操作中,CRUD是最核心、最基础的操作,贯穿了数据管理的整个生命周期。CRUD分别对应Create(新增)、Retrieve(查询)、Update(更新)、Delete(删除)四个的基本操作,掌握这些操作是学好MySQL的最重要的一步。
数据库的层级结构
在开始学习CRUD操作之前,我们需要先了解数据库的基本层级结构:
数据库服务器 --> 多个数据库 --> 多张数据表 --> 多个数据行 --> 多个列(字段)
我们日常操作的主要对象是数据表中的记录(即多个数据行),每张表由若干列(字段)组成,每个字段定义了数据的类型和属性。理解这一结构有助于我们更好地理解CRUD操作的作用对象和范围。
一、新增(Create)
新增操作用于向数据表中插入新的记录。在SQL中,使用INSERT
语句实现新增功能,根据插入数据的行数和指定字段的不同,可分为多种插入方式。
1. 单行数据 + 全列插入
全列插入是指在插入数据时,指定表中所有字段的值。语法如下:
insert into 表名 (字段1,字段2,...) values (值1,值2,...);
说明:
字段列表需要与数据表定义的字段名一致
values中的值需要与前面指定的字段顺序一一对应
字符串类型的值必须用英文单引号包裹
例如:
向student表中插入一条完整记录:
insert into student (id, name, age) values (1, '张三');
上述语句中,字段列表包含了student表的所有字段,values列表中的值与字段一一对应,分别为id、name字段赋值。
注意:如果字段与值的数量不匹配,会导致插入失败并报错
例如:字段列表有2个字段,而values列表只有2个值
如果列与值的个数据不匹配就会提示"列数不匹配"的错误,并且这条记录无法写入数据库。
2. 单行数据的简写插入
当插入的数据包含表中所有字段,不用在表名后指定列名,在values列表中按表中定义字段的顺序设置相对应的值,可以省略字段列表,直接使用简写形式:
insert into 表名 values (值1,值2,值3...);
例如,上述插入student表的语句可简写为:
insert into student values (1, '张三', 18, '男');
注意:这种简写方式要求values中的值必须严格按照表定义的字段顺序排列,且数量必须与表中字段数量完全一致,否则会导致插入失败。
3. 指定列插入
在实际应用中,我们经常不需要为表中所有字段赋值,这时可以使用指定列插入的方式,只为需要的字段赋值,未指定的字段将使用默认值(通常为NULL)。语法如下:
insert into 表名 (字段1, 字段2, ...) values (值1, 值2, ...);
例如,向student表中只插入name字段的值:
insert into student (name) values ('王五');
执行上述语句后,id未指定的字段将使用默认值NULL。
适用场景:
- 表中包含自增字段(如id),插入时不需要手动指定
- 某些字段有默认值,不需要手动赋值
- 只需要为部分字段设置初始值
4. 多行数据插入
当需要插入多条记录时,可以使用多行数据插入方式,一次性插入多条记录,提高操作效率
语法如下:
insert into 表名 [(指定列...)] values
(值1[,值1...]),
(值2[,值2...]),
(值3[,值3...])...;
指定列可以指定多个列;一条insert语句在values部分,可以跟很多的values组,每一组表示要插入的一个数据行 。
例如,向student表中插入三条记录:
insert into student (id,name) values
(4,'赵六'),
(5,'孙七'),
(6,'王麻子');
多行插入与单行插入的效率对比:
一次插入多条数据比多次插入单条数据效率更高,主要原因如下:
- 网络开销:每执行一次SQL语句都需要与数据库服务器进行网络通信,多次执行会增加网络传输次数和时间
- 磁盘IO开销:写入数据需要进行磁盘操作,批量写入可以减少磁盘IO次数
- 事务开销:每执行一条SQL语句都会开启一个事务,事务的开启和关闭需要消耗系统资源,批量插入可以减少事务处理的开销
因此,在实际开发中,推荐使用多行插入方式批量添加数据(在合理范围内,避免单次插入数据量过大)。
一次提交多条数据(在一个可控范围之内),比一次提交的一条数据效率高一点
二、查询(Retrieve)
查询操作是数据库中使用最频繁的操作,用于从数据表中获取所需的数据。SQL提供了丰富的查询功能,可通过SELECT
语句实现各种复杂的查询需求。
1. 全列查询
全列查询用于获取表中所有记录的所有字段。语法如下:
select * from 表名;
其中,*
表示的是要查询表中的所有的列。
示例:查询exam表中的所有记录:
select * from exam;
注意:全列查询在生产环境中是非常危险的操作
原因如下:
1. 生产环境中表的数据量可能非常大(可达TB级或亿级记录),全列查询会消耗大量的磁盘和网络资源
2. 大量的数据传输和处理可能占用服务器全部资源,导致其他程序或数据库操作就要等待当前SQL执行完之后才能继续执行
3. 可能返回大量不需要的字段和记录,浪费系统资源
因此,在生产环境中应避免使用不加限制的全列查询,如需使用,应通过限制条件控制返回结果的数量。
2. 指定列查询
指定列查询用于获取表中指定字段的记录,只返回需要的字段,减少数据传输量。
语法如下:
select 列名1[,列名2...] from 表名;
示例:查询exam表中的id、name和语文成绩:
select id, name, chinese from exam;
这种查询方式的优势在于:
- 减少网络传输的数据量
- 提高查询效率
- 只返回需要的信息,便于数据处理
3. 查询结果为表达式
在查询时,除了直接返回表中的字段值,还可以返回基于字段的表达式结果。表达式可以是常量、单个字段的运算或多个字段的运算。
(1)不含字段的表达式
查询时可以添加常量表达式,作为结果集中的一列。例如:
select id, name, chinese, 10 from exam;
上述语句会在查询结果中添加一列,所有行的该列值均为10。它本身并不在我们的真实的表里;所以这一列是临时生成的,并不存在于原表中。
(2)包含一个字段的表达式
可以对单个字段进行运算,返回运算结果。例如:
select id, name, chinese + 10 from exam;
上述语句会返回每个学生的id、name以及语文成绩加10后的结果。
注意:这种运算只影响查询结果,不会修改表中的原始数据。如果需要修改原始数据,需要使用UPDATE操作,这只是查询方便查看某些总体的数据等
(3)包含多个字段的表达式
可以对多个列的进行运算,返回综合结果。
例如,计算每个学生的总分:
select id, name, chinese + math + english from exam;
为了使查询结果更易读,可以为表达式结果指定别名,使用as
关键字:
select id, name, chinese + math + english as 总分 from exam;
as
关键字可以省略,直接写别名:
select id, name, chinese + math + english 总分 from exam;
如果别名包含空格或特殊字符,需要用单引号包裹:
select id, name, chinese + math + english '总 分' from exam;
注意:通过表达式查询生成的结果集是一个临时表,查询结束后临时表会被自动删除,不会影响原表数据。在MySQL中,所有查询结果都会通过临时表返回给用户。
4. 别名
在查询时,可以为字段或表达式指定别名,使查询结果更易读。语法如下:
select 列名 [as] 别名 [,列名 [as] 别名]… from 表名;
示例:
select id as 学号, name 姓名, chinese 语文成绩,math 数学成绩,english 英语成绩
from exam;
使用别名的优势:
使查询结果的列名更直观、易理解;简化复杂表达式的显示;便于在查询中引用表达式结果(如排序时)
5. 去重(distinct)
去重操作用于移除查询结果中的重复记录,只保留唯一的记录。使用distinct
关键字实现,语法如下:
select distinct 列名 from 表名;
示例:查询exam表中所有不重复的数学成绩:
select distinct math from exam;
注意:
MySQL中,只有当查询结果中所有列的值都相同时(也就是数据行与数据行之间内容完全一致),才会被视为重复记录;去重后,重复的记录只会保留一条
例如,查询id和math字段时:
select distinct id, math from exam;
可以看到没有去重成功,是因为两条记录的其中的id不同,id字段所在的列不重复,distinct 也就不把它们当做相当的行,导致两条记录没有完全相同
只有当两条记录的所有查询字段值都完全相同时,才会被去重。例如,插入两条完全相同的记录:
示例:
在这个表中插入一条数据:
这次再去执行查询去重操作
只保留了一条记录
去重时,只有查询结果中所有的列都相同才会被认定为重复记录
去重后,重复记录只保留一条
6. 排序(order by)
排序操作用于对查询结果按照我们指定的规则对结果进行排序。使用order by
子句实现,语法如下:
select 列名 from 表名 order by 列名 [ASC | DESC];
其中:
ASC
表示升序排序(默认值,可省略)DESC
表示降序排序
提示:
查看表结构用到了desc describe描述
排序中desc descend下降
示例:查询exam表中的记录,按语文成绩升序排序:
select * from exam order by chinese;
-- 等价于
select * from exam order by chinese ASC;
按语文成绩降序排序:
select * from exam order by chinese DESC;
指定了排序的列,返回的结果就是针对这个列进行排序后的结果集
没有写排序规则的时候就是默认是升序排序
(1)排序的注意事项
当没有指定order by
子句时,MySQL不保证返回结果的顺序,永远不要依赖默认排序返回给结果集顺序,默认MySQL根据哪个字段进行排序,是不确认的;
如果需要特定的排序顺序,必须明确指定order by
子句
(2)NULL值的排序规则
- 在数值排序时,NULL被视为比任何数值都小
- 升序排序(ASC)时,NULL值出现在最前面
- 降序排序(DESC)时,NULL值出现在最后面
示例:查询包含NULL值的字段并排序:
-- 升序排序,NULL值在最前面
select * from exam order by english ASC;-- 降序排序,NULL值在最后面
select * from exam order by english DESC;
(3)使用表达式及别名排序
可以使用表达式或字段的别名进行排序。例如,按总分降序排序:
-- 使用表达式排序
select id, name, chinese + math + english from exam order by chinese + math + english desc;-- 使用别名排序(推荐)
select id, name, chinese + math + english as total from exam order by total desc;
使用别名排序更简洁、易读,推荐使用这种方式。
注意:MySQL中,NULL与任何值进行运算的结果都是NULL。例如,上述的孙大圣的英语成绩为NULL,那么计算总分(chinese + math + english)的结果也会是NULL。
NULL始终被判定为FLASE;NULL的值不是我们以前学习过的其他编程语言中的0,在MySQL中他就是NULL
(4)多字段排序
可以对多个字段进行排序,排序优先级按照字段的书写顺序确定。
每个字段可以指定不同的排序规则。语法如下:
SELECT 列名1, 列名2… FROM 表名
ORDER BY 列名1 [asc|desc], 列名2 [asc|desc], 列名3 [asc|desc]…
示例:先按数学成绩降序排序,数学成绩相同的再按语文成绩升序排序,语文成绩也相同的再按英语成绩升序排序:
select id, name, chinese, math, english from exam
order by math desc, chinese asc, english;
排序逻辑分析:
- 首先按照第一个字段(math)的规则(降序)排序
- 当第一个字段的值相同时,按照第二个字段(chinese)的规则(升序)排序
- 当第二个字段的值也相同时,按照第三个字段(english)的规则(默认升序)排序
- 以此类推
优先执行,按先后书写顺序,但是后续的排序是在前一次的有重复的数据时再进行排序,没有重复的话,不会对其最终的排序产生影响,前次排序就可以确定顺序
多字段排序适用于需要更精细排序规则的场景,确保结果的有序性符合业务需求。
7. 条件查询(where)
条件查询用于根据指定的条件过滤记录,过滤掉不符合条件的记录,把符合条件的记录返回给用户。使用where
子句实现,可结合比较运算符和逻辑运算符实现复杂的条件过滤。
(1)比较运算符
常用的比较运算符如下:
运算符 | 说明 |
---|---|
>, >=, <, <= | 大于,大于等于,小于,小于等于 |
= | 等于(NULL不安全,例如null = null的结果是NULL) |
<=> | 等于(NULL安全,例如NULL<=>NULL的结果是TRUE(1)) |
!=, <> | 不等于(两种写法都可以) |
between a0 and a1 | 范围匹配,[a0, a1],如果a0 <= value <= a1,返回TRUE(1) |
in (option, …) | 如果值是option中的任意一个,返回TRUE(1) |
is null | 判断是否为NULL |
is not null | 判断是否不为NULL |
like | 模糊匹配,%表示任意多个字符(包含0个),_表示任意一个字符 |
说明:
=
和<=>
的区别:=
不能正确判断NULL值,而<=>
可以。例如,null = null
的结果是NULL,而null <=> null
的结果是1(TRUE)。between a0 and a1
是闭区间,包含a0和a1两个端点值。in
用于判断值是否在指定的集合中,等价于多个or
条件的组合。like
用于模糊查询,%
匹配任意长度的字符串(包括0长度),_
匹配恰好一个字符。- 在其他编程语言中判断相等用的是
==
,MySQL中用的是=
,以后的赋值用的也是=
对于is / is not null的补充说明:
IS NULL和IS NOT NULL都是针对于NULL的判断,
IS NULL如果是NULL返回true,否则false
IS NOT NULL如果是NULL返回false,否则返回true
in 表示查询的数据在in后面的数据集中是否存在,若存在则返回1,若不存在则返回0
说明:条件中的值在选项列表中存在则返回true,否则返回false,option是一个列表或是集合
like:模糊匹配
select * from exam where name like '孙%';
select * from exam where name like '孙_';
select * from exam where name like '孙__';
_
表示相当于一个占位符,有几个_就必须匹配几个字符
(2)逻辑运算符
常用的逻辑运算符如下:
运算符 | 说明 |
---|---|
and | 逻辑与&& ,多个条件同时满足 |
or | 逻辑或|| ,多个条件中至少一个满足 |
not | 逻辑非! ,取反 |
优先级:not
> and
> or
,可以使用括号改变运算顺序。
(3)条件查询的语法
select 列名 from 表名 where 列名/表达式 运算符 条件;
条件查询时,首先要确定好给哪个列设置相应的条件
(4)条件查询示例
示例1:查询英语成绩不合格的同学(<60)
select name from exam where english < 60;
注意:结果集中不会包含英语成绩为NULL的记录,因为NULL与任何值比较的结果都为NULL(视为false),所以也就是自动过滤掉了值为null的列。
示例2:查询语文成绩好于英语成绩的同学
在每一行的数据中的两个列是可以进行比较的,但是不能跨行比较
select * from exam where chinese > english;
该查询会比较每一行的语文成绩和英语成绩,最终把语文成绩大于英语成绩的记录加入到结果集中返回给用户
示例3:查询总分在200分以下的同学
select name, chinese + math + english from exam where chinese + math + english < 200;
注意:where子句中不能使用别名作为过滤条件,因为MySQL的执行顺序是先执行where子句,再执行select子句生成别名。因此,必须在where子句中完整写出表达式。
select name, chinese+math+english as '总分' from exam where (english+math+chinese) < 200 order by '总分' asc;
执行顺序:
1.如果需要在数据中查某些数据,首先要确定表,先执行from
2.在查询的过程中要根据指定的查询条件把符合条件的数据过滤出来,这是执行的就是where子句
3.执行select后面的指定的列,这些列需要加入到最终的结果集中
4.排序操作,根据order by子句中的指定的列名和排序规则进行最后的排序
示例4:查询语文成绩大于80分且英语成绩大于80分的同学
select * from exam where english > 80 and chinese > 80;
示例5:查询语文成绩大于80分或英语成绩大于80分的同学
select * from exam where chinese > 80 or english > 80;
只要满足一个条件就可以,就符合整个的查询条件
示例6:观察AND和OR的优先级
-- and优先级高于or
select * from exam where chinese > 80 or math > 70 and english > 70;-- 使用括号改变优先级
select * from exam where (chinese > 80 or math > 70) and english > 70;
-- 优先级:
NOT > AND > OR
建议使用括号明确指定运算顺序,提高代码可读性,避免因优先级问题导致的错误。
示例7:查询语文成绩在[80, 90]分的同学
-- 使用between
select * from exam where chinese between 80 and 90;-- 等价于
select * from exam where chinese >= 80 and chinese <= 90;
对数据进行范围过滤,用这两种方式都可以注意,左右闭区间
示例8:查询数学成绩是58、59、98或99分的同学
-- 使用in
select name, math from exam where math in (58, 59, 98, 99);-- 等价于
select name, math from exam where math = 58 or math = 59 or math = 98 or math = 99;
两种方式都可以实现需求,在实际工作中根据当时的情况选一个合适就行
示例9:模糊查询
%
匹配任意多个(包括 0 个)字符{匹配字符没有限制}
_
匹配严格的一个任意字符
-- 查询姓孙的同学(孙后面可以跟任意多个字符)
select * from exam where name like '孙%';-- 查询名字为两个字且姓孙的同学
select * from exam where name like '孙_';-- 查询名字中包含"小"字的同学
select * from exam where name like '孙__';
示例10:查询NULL值
-- 查询英语成绩为NULL的同学
select * from exam where english is null;-- 或者使用<=>
select * from exam where english <=> null;-- 查询英语成绩不为NULL的同学
select * from exam where english is not null;
注意:判断NULL值时,不能使用=
或!=
,必须使用is null
、is not null
或<=>
。
8. 分页查询(limit)
分页查询用于限制查询结果的数量,只返回部分记录;
分页查询在项目中运行的非常多,只要查询的是一个记录的集合(多条记录)都在使用分页查询;使用limit
子句实现,可有效减少数据库服务器的压力,提高查询效率。
通过分页查询可以有效地控制一次查询出来的结果集中的记录的条数,可以有效的减少数据库服务器的压力,同时对于用户也比较友好
(1)分页查询的语法
-- 从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;
其中:
n
表示要返回的记录数s
表示起始位置(从0开始计数)offset
表示偏移量,即从第s条记录开始
(2)分页查询示例
示例1:返回前2条记录
select * from exam limit 2;
-- 等价于
select * from exam limit 2 offset 0;
示例2:从第2条记录开始,返回3条记录
select * from exam limit 2, 3;
-- 等价于
select * from exam limit 3 offset 2;
示例3:分页查询公式
在实际应用中,分页查询通常需要根据页码和每页记录数计算起始位置。公式如下:
起始位置 s = (当前页码 - 1) * 每页记录数
例如,每页显示10条记录:
- 第1页:s = (1-1)*10 = 0,查询语句:
limit 10 offset 0
- 第2页:s = (2-1)*10 = 10,查询语句:
limit 10 offset 10
- 第3页:s = (3-1)*10 = 20,查询语句:
limit 10 offset 20
注意:如果起始位置超出了结果集的范围,查询会返回空结果集,不会报错。
三、修改(Update)
修改操作用于更新表中已存在的记录。使用UPDATE
语句实现,可以更新单个字段或多个字段的值。
1. 修改操作的语法
update table_name set column1 = expr1 [, column2 = expr2 ...]
[where ...] [order by ...] [limit ...]
其中:
table_name
是要修改的表名column = expr
表示要更新的字段和对应的新值,可以同时更新多个字段,用逗号分隔where
子句用于指定修改的条件,只更新符合条件的记录order by
子句用于指定更新的顺序limit
子句用于限制更新的记录数量
2. 修改操作示例
示例1:将孙悟空同学的数学成绩变更为80分
update exam set math = 80 where name = '孙悟空';
如果表中有多条姓名为"孙悟空"的记录,上述语句会更新所有符合条件的记录。
注意:如果不加where条件,会更新表中的所有记录,这是非常危险的操作,可能导致大量数据被误修改,务必谨慎操作。
-- 危险!会更新表中所有记录的math字段
update exam set math = 100;
示例2:将曹孟德同学的数学成绩变更为60分,语文成绩变更为70分
update exam set math = 60, chinese = 70 where name = '曹孟德';
同时更新多个字段时,字段之间用逗号分隔。
示例3:将总成绩倒数前三的同学的数学成绩减去30分
update exam set math = math - 30
order by chinese + math + english asc
limit 3;
该语句先按总成绩升序排序(即倒数顺序),然后取前3名,将他们的数学成绩减去30分。
注意:MySQL中不支持+=
、-=
、*=
、/=
等自增自减运算符,必须使用column = column + value
的形式。
示例4:将语文成绩小于50的同学的语文成绩更新为原来的2倍
update exam set chinese = chinese * 2 where chinese < 50;
执行该语句后,只有语文成绩小于50且更新后的值发生变化的记录会被实际修改。如果原始值为0,更新后仍为0,则不会被视为修改。
只要是实际的值发生了改变,那么磁盘的值就会发生改变,如果未发生改变,那么就不会发生改变
四、删除(Delete)
删除操作用于从表中移除记录。使用DELETE
语句实现,可以删除符合条件的记录或清空表。
1. 删除操作的语法
delete from table_name [where ...] [order by ...] [limit ...]
其中:
table_name
是要删除记录的表名where
子句用于指定删除的条件,只删除符合条件的记录order by
子句用于指定删除的顺序limit
子句用于限制删除的记录数量
2. 删除操作示例
示例1:删除孙悟空同学的考试成绩
delete from exam where name = '孙悟空';
原数据:
修改完成的:
该语句会删除所有姓名为"孙悟空"的记录。
示例2:删除英语成绩倒数前三的同学的所有考试成绩
delete from exam
order by english asc
limit 3;
再观察被操作后的数据内容:
该语句先按英语成绩升序排序(即倒数顺序),然后删除前3名的记录。
注意:如果删除时不加where条件,会删除表中的所有记录(清空表),这是非常危险的操作,可能导致数据丢失。
-- 危险!会删除表中所有记录
delete from exam;
虽然删除的数据可以通过数据库日志恢复,每一条执行的SQL都会被记录到日志中,把日志中记录的操作,在执行一遍基本上就可以完成恢复;但恢复过程复杂且耗时,因此应尽量避免执行此类操作。
3. 生产环境中的删除策略
在生产环境中,一般不推荐使用DELETE
语句直接删除数据,而是采用逻辑删除的方式:
- 在表中添加一个标记字段(如
delete_state
),用于表示记录是否被删除 - 0表示记录正常(未删除),1表示记录已删除
- 删除操作实际上是更新该标记字段的值,而不是物理删除记录
例如:
-- 添加标记字段
alter table exam add column delete_state tinyint(1) default 0;-- 逻辑删除操作(更新标记字段)
update exam set delete_state = 1 where name = '孙悟空';-- 查询时过滤已删除的记录
select * from exam where delete_state = 0;
总结:
CRUD操作是数据库的基础,掌握这些操作是进行数据库开发和管理的前提。这期内容介绍了新增(Create)、查询(Retrieve)、更新(Update)、删除(Delete)四种操作的语法、使用场景和注意事项等
在实际应用中,应根据业务需求选择合适的操作方式,同时注意操作的安全性,避免误操作导致的数据丢失或损坏。通过不断练习和实践,才能熟练掌握这些基础操作,为更复杂的数据库操作打下坚实的基础。