MySQL相关概念和易错知识点(3)(表内容的CURD、内置函数)

发布于:2025-08-02 ⋅ 阅读:(8) ⋅ 点赞:(0)

CURD,即增(create)删(delete)查(read)改(update)。上一篇文章主要分享的是表结构的修改,本篇文章会进一步讲讲如何对内容做管理,毕竟这涉及到MySQL的核心功能——数据存储

因为MySQL中同一个关键字可能有多重功能,所以这里根据关键字分类,从常用的开始。

1.insert

(1)插入功能

insert最常用的就是插入功能,可实现全列插入、指定列插入、多行插入、单行插入

insert into tb values (1, '小红'); # 不指定列,按默认顺序插入,即全列插入
insert into tb(name, id) values ('小绿', 2); # 按指定顺序插入
insert into tb(name) values ('小蓝'); # 按指定顺序缺省式插入,需保证缺省参数可为空或有默认值
insert into tb values (4, '小黄'), (5, '小橙'); # 多行插入,也可像上面那样按指定列顺序多行插入

(2)更新功能

插入比较常用,也很直观,所以不再详述。insert还有更新的功能,有时我们会遇到一个场景,就是我们插入的表有唯一键或主键,而插入的内容和它的唯一性冲突了,但这时我们并不是插错了,而是想要将对应的数据进行一次更新,这时就要在后面加上一些东西了。

insert into tb values (1, '小紫'); # 一般的插入,id是主键,这时如果主键冲突会直接报错
insert into tb values (1, '小紫')
on duplicate key update name = '小粉';

我们可以看到id=1对应的位置name被改成了小粉,并不是小紫。这说明当触发主键冲突之后,insert前半部分定位到了对应的行,之后便执行下面的更新语句,更新的内容以下面的为准,上面的失效。如果没有发生主键冲突,那就是以前半部分语句为主,后面的更新语句失效。 我们可以根据执行语句的结果来判断:

– 0 row affected: 表中有冲突数据,但冲突数据的值和 update 的值相等
– 1 row affected: 表中没有冲突数据,数据被插入,update 后面的语句失效,仅执行前半部分
– 2 row affected: 表中有冲突数据,数据被更新,这时只会执行后半部分,前半部分失效

我们可用row_count查看受影响的行数

在这里插入图片描述

但我们更新时希望用上前半部分的数据,否则再写一遍也很麻烦,所以我们可以用values(name)的方式复用前半部分语句的值,因此我们还可以用另一种办法给改回来。

insert into tb values (1, '小红')
on duplicate key update name = values(name); # 这里的name直接复用上面的值

在这里插入图片描述

总的来说,insert主要还是用于插入。当和唯一键或主键冲突时,会根据values定位行,并执行on duplicate key update后面的语句进行更新,更新时我们可以复用前半部分指定的值。

(3)从A表提取数据并存入B表

我们可以根据不同需求从一张表提取数据并放入另一张表中,从而实现去重、备份或者对其它表进行提取等操作。

insert into tb20 select distinct * from tb19;

在这里插入图片描述

2.select

查询是除增加以外同样重要的功能,select关键字是首选。但select并不只是一个查询的关键字,它可以用来参与运算,执行内置函数等操作,在复合查询中非常好用,接下来会详细讲解它。

(1)内置函数

select可以作为函数调用的关键字,比如查询当前我们在哪个database,当前时间,上一次MySQL修改行数等,它能尽快帮助我们获取当前MySQL的状态,或是帮助我们实现某些功能(如协助插入修改等)。

a.时间函数

下面是常见的日期函数,可以进行日期计算等。关于日期,事实上系统底层获取的时间都是完整格式的,只是返回的值根据date、time、timestamp的格式不一样。所以传参数时我们无需考虑这是什么格式的时间,只要是时间相关的都能参与运算。

使用实例

select current_date(); # yy-mm-dd 格式
select current_time(); # hh:mm:ss 格式
select current_timestamp(); # 完整时间格式,年月日时分秒
select date(now()); # 获取当前时间的 yy-mm-dd 格式
select date_add(now(), interval 3 day); # 当前时间加上3天,返回完整时间格式,年月日时分秒
select date_sub(now(), interval -4 year); # 当前时间加上4年
select datediff(now(), date_add(now(), interval 3 day)); # 得到-3,天数是前面的减去后面的
select now(); # 获取当前完整时间,年月日时分秒

单独调用select可以执行这些函数,但这些函数更多是和查询结合起来,用于实现一些复合查询,如在很多时候我们需要在where子句中利用日期判断来进一步查询。

b.字符串函数

字符串函数主要是用于规范化处理,或是用于拼接展示等。 下面是它们的使用实例

select charset('string'); # 本机测试结果是utf8mb4,具体返回的根据当前列采用的编码方式有关
select concat('I', ' ', 'am', ' ', 'happy!'); # 连接字符串,结果为I am happy!
select instr('happy', 'py'); # 返回4,下标从1开始算
select ucase('apple'); # 字母全部转为大写,非字母不会处理
select lcase('apple'); # 字母全部转为小写,非字母不会处理
select left('pencil', 3); # 结果为pen
select length('pencil'); # 长度为6,没有C语言中的\0的概念
select replace('public', 'bl', '_private_'); # 结果为pu_private_ic
select strcmp('APple', 'Zapple'); # 结果是-1,比较的是字符串长度,前面长则为1,一样长则为0,后面长则为-1
select substring('apple', 3); # 返回ple
select trim( '      I am happy'); # 返回I am happy,ltrim消除前空格,rtrim消除后空格

c.数学函数

使用示例

select abs(-110); # 返回110
select bin(6); # 返回110
select hex(10); # 返回A
select conv(16, 10, 17); # 将16从10进制转为17进制,返回G
select ceiling(23.34); # 返回24
select floor(23.99); # 返回23
select format(34, 5); # 返回34.00000
select format(rand() * 100, 0); # 返回一个0~100的随机整数
select mod(7, 2); # 7%2返回1

d.查询当前用户

e.md5摘要(得到一个32位字符串)

f.当前使用数据库

g.ifnull

select ifnull(val1, val2) 如果val1为null,返回val2,否则返回val1的值

h.获取受影响行数

当执行insert、update、delete等修改表内容的语句时,row_count()返回上一次受影响的行数

当执行其它和修改表内容无关的语句时,返回 -1

在这里插入图片描述

i.获取上次自增长插入的值

j.find_in_set

(2)列查询

a.全列和指定列查询

列查询分为全列查询和指定列查询,一般大型数据表下不建议全列查询,否则传输数据非常消耗时间(mysqld和mysql多数情况下通过网络传输)

select * from tb; # 全列查询
select name, id from tb; # 指定列查询

b.计算表达式+起别名查询

对于select来说,select name, id from tb;这个语句中name、id是被分别当作表达式处理的。 name单独作为一个表达式就代表这个列,我们也可以将name换成其它对象,select都能处理。


如果是期末考试的成绩表,班主任就可以拿着各科的成绩用Chinese + Math + English得到每个同学的总分了,计算该表达式会针对每一行单独计算。同时,我们也希望最后显示的时候列名为“总分”,所以我们还需要对该列起别名。

我们直接在表达式后面空格后写上别名即可

c.去重

在有的时候,我们希望对查询到的值进行去重,在select关键字后添加distinct即可。注意去重是根据后面的表达式进行的,它的逻辑是先不去重,把所有表达式处理完形成一个表结构,再去除完全相同的行。

在这里插入图片描述

(3)where子句

通过select id, name表达式我们可以筛选列,并且可以对列进行运算等,那么where就是对行进行筛选。通过select + where,我们就可以实现对列、行进行全面筛选,可以保证得到的结果就是我们想要的。

where子句既然是对行进行筛选,靠什么筛选?无非是针对某行对应的某些列的值进行筛选,比如英语成绩大于语文成绩,总分高于750,英语成绩是数学成绩的20倍等。我们发现,这些筛选条件需要用逻辑相连,与数学表达式强相关,所以前面我们学习的数学表达式,以及下面的运算符都是where子句不可缺少的一部分。

下面是常用的运算符表
在这里插入图片描述
这些运算符就是连接行筛选条件的工具,下面会对它们每个进行举例,学会它们我们就几乎可以对任意条件进行处理了,无非是简单和复杂的区别,本质都是逻辑运算的拼接

接下来会在这张表的基础上进行演示

-- 1. >,>=,<,<= 
select * from tb18 where Chinese > 80; # >,>=,<,<= 大于、大于等于、小于、小于等于  

-- 2. = (等于,NULL 不安全)
select * from tb18 where Math = 90; # = 等于,NULL 不安全(如 NULL = NULL 结果为 NULL)  

-- 3. <=> (等于,NULL 安全)
select * from tb18 where Chinese <=> null; # <=> 等于,NULL 安全(如 NULL <=> NULL 结果为 TRUE(1))  

-- 4. !=,<> (以 <> 为例)
select * from tb18 where Chinese <> 85; # !=,<> 不等于  

-- 5. between a0 and a1 (范围匹配)
select * from tb18 where Math between 80 and 90; # between a0 and a1 范围匹配 [a0,a1],若 a0 <= value <= a1 则返回 TRUE(1)  

-- 6. in (option, ...) (值匹配)
select * from tb18 where English in (88, 90, 92); # in (option,...) 值匹配任意 option 则返回 TRUE(1)  

-- 7. is null (判断 NULL)
select * from tb18 where Chinese is null; # is null 判断值为 NULL  

-- 8. is not null (判断非 NULL)
select * from tb18 where Chinese is not null; # is not null 判断值不为 NULL  

-- 9. like 
select * from tb18 where name like '%张%'; # like 模糊匹配:% 匹配任意长度字符(含空);_ 匹配单个字符  
select * from tb18 where name like '_五'; # 匹配“王五”

-- 10. and (多条件同时满足)
select * from tb18 where Chinese > 80 and Math > 85; # and 多条件全为 TRUE(1) 时结果为 TRUE(1)  

-- 11. or (多条件任意满足)
select * from tb18 where Chinese < 70 or English < 80; # or 多条件任意一个为 TRUE(1) 时结果为 TRUE(1)  

-- 12. not (条件反转)
select * from tb18 where not (Math < 90); # not 条件为 TRUE(1) 时结果反转(返回 FALSE(0))  

-- 13. 带括号的符合查询
select * from tb18 where Chinese > 80 and (Math > 85 or English > 90); 

通过运算符和逻辑拼接,我们可以实现任何形式的行筛选,再结合列筛选,我们已经能够得到想要的行和列了。除此之外,我们还可以对最后的结果进行排序,这就涉及到order by语句了,下面也会进一步分析select后子句多的情况下的执行顺序,以及我们需要注意的事项。

(4)order by

如果我们选择的校验集和字符集不区分大小写,order by也不会区分。

select id, name from tb18 where Chinese > 76 order by id desc; # 选出语文成绩大于76的人,按id降序排列
select id, name from tb18 where Chinese > 76 order by name asc; # 选出语文成绩大于76的人,按名字升序排列

在这里插入图片描述
我们最好显式写asc或desc,不要依赖默认顺序。

order by也能连用,如order by Math desc, Chinese asc,意思就是先按Math降序排列,Math相同的按照Chinese升序排列。

(5)limit

通过select和where,我们能分别实现对列和行的筛选;通过order by我们也能按照想要的顺序展示结果。 但是还剩一个问题,如果筛选后的数据量还是太大怎么办?分页。就像我们平时上网那样,限制一页行数,通过多页来进行展示,这样就可以防止大量数据传输和刷新,limit就可以实现这个功能。

limit有几个易混的用法,这里仅展示较为清晰的一种。在order by子句后,limit n表示限制展示的行数。

在这里插入图片描述

限制单页的行数有了,但是分页效果呢?offset表示偏移量,在这里可用作行的定位。从第0行开始,也就是说limit 3 offset 0和上面的例子等价。

在这里插入图片描述
我们可以借助这一点实现翻页。

(6)where和order by执行顺序

在某些场景下,select的执行顺序决定了我们语句的书写方式,主要涉及到列起新名时书写问题。

from先执行(必须先找到表,找到表才能有后面的操作)、再执行where(先筛选行)、再执行select的筛选(根据已筛选的行再筛选列)、得到筛选结果后进行排序(order by在select之后,能够得到重命名结果)

-- where 执行顺序问题
select id 学号, name 姓名, Chinese + Math + English 总分 from tb18 where Chinese + Math + English is not null;
select id 学号, name 姓名, Chinese + Math + English 总分 from tb18 where 总分 is not null;

-- order by 执行顺序问题
select id 学号, name 姓名, Chinese + Math + English 总分 from tb18 order by Chinese + Math + English asc;
select id 学号, name 姓名, Chinese + Math + English 总分 from tb18 order by 总分 asc;

where中不能用别名,因为语句在重命名之前执行

order by可以使用别名,因为语句在重命名之后执行

3.update、replace

(1)update

update和select高度相似,借助where子句定位行,set列来进行进一步的值修改。

update tb18 set Math = 100, English = 100 where name = '张三';


除了基本的更新,我们也可全局更新,例如给全班每人的成绩都减去1分。

update tb18 set Chinese = Chinese - 1, Math = Math - 1, English = English - 1; # 不能使用 -= 

在这里插入图片描述
在更多时候,我们会更细粒度地去更新,例如给班上倒数3名同学的英语成绩加上10分

update tb18 set English = English + 10 order by Chinese + Math + English asc limit 3; # limit在这里不能分页

(2)replace

如果我们想要对一整行数据进行替换而不是一个一个更新,可以考虑replace,它和insert的用法一致。

replace into tb18(id, class, name, Chinese, English) values(6, 3, '周八', 50, 50);

注意替换后整行数据会被先删除再增加,所以要写完整。

4.delete、truncate

(1)delete

delete用于删除对应的行,其中也需要where子句进行定位。

delete from tb; # 删除一整张表
delete from tb where name = '小红'; # 删除定位的行

delete删除行后,自增长的值不会发生变化,此后的插入依然按照删除前规则进行。

(2)truncate

truncate和delete都是用于删除内容操作,区别是delete可以像上述那样精细操作,也不会修改自增长的值。而truncate只能对整张表内容进行删除操作(表结构还在,delete和truncate均不能对表结构进行修改),并且它会重置自增长的值。

truncate从效率上说比delete更快,因为truncate不会经过事务,因此操作同样无法回滚。

还有个细节,truncate后结果显示0 rows affected,说明没有行受到影响,这其实和truncate的实现有关。truncate是直接把原表结构删掉,再新建一个表格替换,而不是像delete那样对表内容做操作。


网站公告

今日签到

点亮在社区的每一天
去签到