【MySQL】增删查改的艺术——数据库CRUD完全指南(上)

文章目录

  • [1. Create(增)](#1. Create(增))
    • [1.1 单行数据+指定列插入](#1.1 单行数据+指定列插入)
    • [1.2 单行数据+全列插入](#1.2 单行数据+全列插入)
    • [1.3 多行数据+全列/指定列插入](#1.3 多行数据+全列/指定列插入)
    • [1.4 插入时更新](#1.4 插入时更新)
    • [1.5 替换插入](#1.5 替换插入)
  • [2. Read/Retrieve(查)](#2. Read/Retrieve(查))
    • [2.1 SELECT 列](#2.1 SELECT 列)
      • [2.1.1 全列查询](#2.1.1 全列查询)
      • [2.1.2 指定列查询](#2.1.2 指定列查询)
      • [2.1.3 查询字段为表达式](#2.1.3 查询字段为表达式)
      • [2.1.4 为查询结果指定别名](#2.1.4 为查询结果指定别名)
      • [2.1.5 结果去重](#2.1.5 结果去重)
    • [2.2 条件查询------where子句](#2.2 条件查询——where子句)
      • [2.2.1 英语不及格的同学及英语成绩 ( < 60 )](#2.2.1 英语不及格的同学及英语成绩 ( < 60 ))
      • [2.2.2 语文成绩在 [80, 90] 分的同学及语文成绩](#2.2.2 语文成绩在 [80, 90] 分的同学及语文成绩)
      • [2.2.3 数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩](#2.2.3 数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩)
      • [2.2.4 姓孙的同学/孙某同学(模糊匹配)](#2.2.4 姓孙的同学/孙某同学(模糊匹配))
      • [2.2.5 语文成绩高于英语成绩的同学](#2.2.5 语文成绩高于英语成绩的同学)
      • [2.2.6 总分在 200 分以下的同学](#2.2.6 总分在 200 分以下的同学)
      • [2.2.7 别名不能用在where子句中](#2.2.7 别名不能用在where子句中)
      • [2.2.8 语文成绩 > 80 并且不姓孙的同学(AND + NOT)](#2.2.8 语文成绩 > 80 并且不姓孙的同学(AND + NOT))
      • [2.2.9 孙某同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80](#2.2.9 孙某同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80)
      • [2.2.10 NULL 的查询](#2.2.10 NULL 的查询)
    • [2.3 结果排序------order by子句](#2.3 结果排序——order by子句)
      • [2.3.1 同学姓名及其数学成绩,按数学成绩升序/降序显示](#2.3.1 同学姓名及其数学成绩,按数学成绩升序/降序显示)
      • [2.3.2 null在排序中的地位](#2.3.2 null在排序中的地位)
      • [2.3.3 查询同学各门成绩,依次按 数学降序,英语升序,语文升序的方式显示](#2.3.3 查询同学各门成绩,依次按 数学降序,英语升序,语文升序的方式显示)
      • [2.3.4 查询同学及总分,总分升序排序](#2.3.4 查询同学及总分,总分升序排序)
      • [2.3.5 order by 子句中可以使用别名](#2.3.5 order by 子句中可以使用别名)
      • [2.3.6 查询姓孙的同学或者姓曹的同学的数学成绩,结果按数学成绩降序](#2.3.6 查询姓孙的同学或者姓曹的同学的数学成绩,结果按数学成绩降序)
    • [2.4 结果分页](#2.4 结果分页)

什么是CRUD:

CRUD 是指对数据库中表数据的"增、删、查、改"四种基本操作。
C R U D = Create(创建) + Read(读取) + Update(更新) + Delete(删除)

1. Create(增)

语法:

sql 复制代码
INSERT [INTO] table_name
[(column [, column] ...)]
VALUES (value_list) [, (value_list)] ...
value_list: value, [, value] ...

insert在我们之前的文章已经用过很多次了,可以指定列插入或者全列插入。

案例:创建一张学生表

create table student( id int unsigned primary key auto_increment, sn int unsigned unique comment "学号", name varchar(20) not null, qq varchar(32) unique key );

1.1 单行数据+指定列插入

演示一下:

由于这里id为主键列且我们设置了auto_increment属性,所以我们不插,会默认填充递增值。
那我们就指明后三列进行插入
insert into student (sn,name,qq) values(100,'张飞','23435454');
注意:你后面插入的具体值的顺序要和前面指明的列的顺序一一对应

即values的左边是你要插入的列的列名,右边是具体插入的值

1.2 单行数据+全列插入

如果不指明具体要插入的列,则默认为全列插入:

insert into student values(3,101,'刘备','46565555');

并且into可以省略

1.3 多行数据+全列/指定列插入

一次插入多行数据,用逗号隔开即可(指定列也是一样):

insert student values(5,103,'曹操','4776585'), (6,104,'诸葛亮','54884'), (7,105,'许攸','45564');

指定列

1.4 插入时更新

上面的表中:


我们设置了id为主键,sn和QQ为唯一键
所以对于这几列,如果我们插入重复值比如会发生键值冲突

发生键值冲突的时候,可以选择性的进行同步更新操作

语法:

sql 复制代码
INSERT ... ON DUPLICATE KEY UPDATE
column = value [, column = value] ...

举例:


现在执行这条插入语句发生了唯一键冲突,所以报错了。
但是我们可以这样
insert into student (sn,name,qq) values(100,'张清','2303489') on duplicate key update sn=100,name='张清',qq=2303489;

在原语句后面加上红色部分,意思就是如果插入这行数据的时候发生了键值冲突,那就执行后面的更新操作,对这行数据做修改。
当然也要保证你更新的值不能再与另一列发生键值冲突
比如:
首先插入的时候,sn发生了冲突(100),所以执行后面的更新操作,但是QQ要更新的值又与许攸这行数据的QQ这一列发生了键值冲突。

如果正常执行成功,有三种情况:


我们上面的例子

2行受影响,就表明发生了冲突,并更新了冲突行。(原来的没了,更新的插入了,所以2行受影响)
如果没有发生冲突,那就正常插入,后面的更新就不会执行,1行被影响。

如果发生了冲突,但是你更新的和冲突行原来的值一样,那就不用动,0行被影响。

1.5 替换插入

语法:

只需把插入数据中的insert换成replace
replace into ...

不冲突直接插,冲突则替换原来的数据

演示:

不发生冲突:

直接插入(name没有主键或唯一键约束,可以重复),一行受影响
发生冲突

替换原数据(两行受影响,删除原数据,插入新数据)

2. Read/Retrieve(查)

查询相关的语句是我们在MySQL中使用频率最高的,没有之一。

语法:

sql 复制代码
SELECT
[DISTINCT] {* | {column [, column] ...}
[FROM table_name]
[WHERE ...]
[ORDER BY column [ASC | DESC], ...]
LIMIT ...

比较多,下面我们结合具体的场景一一讲解。

案例:

首先还是先来建一个表
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);

2.1 SELECT 列

2.1.1 全列查询

这个我们其实已经很熟悉了

我想查询表中所有列的数据:
select * from exam_result;
* 是通配符,表示匹配所有列

虽然*很方便,但在实际开发中,通常建议明确指定需要查询的列,而不是使用*。原因:

  1. 明确性:明确列出列名使查询更易于理解和维护。
  2. 性能:实际的生产环境中数据库中的数据量一般都非常大,表中有很多列,而只需要其中几列,使用*会不必要地传输更多数据,影响性能。
  3. 可能会影响到索引的使用。(索引后面讲解)

2.1.2 指定列查询

比如:

现在我只想拿到name这一列的所有数据,看看班上就有哪些同学
select 列名 from exam_result;
select name from exam_result;

那一次查询多列呢,比如我要看他们的英语成绩
多个列名之间用逗号隔开即可
select name,english from exam_result;

并且多个列的顺序我们也可以自己调整,可以与原表中的顺序不一致

2.1.3 查询字段为表达式

select后面的查询字段还可以为表达式,什么意思呢?

select 后面跟的就是我们要执行的表达式,这个表达式可以是像上面例子中那些子句。
也可以是比如:

这样的表达式。

比如现在发现数学卷子有道题目出错了,全体学生+10分

2.1.4 为查询结果指定别名

那这就给了我们这样操作的可能:

我们的表中不是有3科的成绩嘛,那现在查询的同时我还想得到每个同学的总成绩
select name,english,math,chinese,chinese+english+math from exam_result;

就可以了
但是,我觉得总分这一列的列名太恶心了,这么长。我想重命名一下,怎么做呢?
select name,english,math,chinese,chinese+english+math as total from exam_result;

原列名 as 新列名即可

而且这个as是可以省略的

2.1.5 结果去重

举例:

我来查询一些所有同学的数学成绩

我们有些同学的数学成绩是一样的,我就想看一下大家的成绩去重之后是怎么样一个分布
那怎么去重呢?
只需加上distinct关键字在要查询的列名之前即可
select distinct math from exam_result;

2.2 条件查询------where子句

上面不论我们是全列查询还是指定列查询,我拿到的都是所有行的数据,但是有些时候我并不想要全部行的数据,可能只想查找某些列符合特定条件下的数据。

这就是我们下面要学的------条件查询

比较运算符:

逻辑运算符:

这些运算符大家先简单看一下,后面通过例子大家就可以更好的理解了

案例:

2.2.1 英语不及格的同学及英语成绩 ( < 60 )

查询英语不及格的同学:

怎么做?
那就使用我们的where子句,后面跟上对应的判断条件即可
select name,english from exam_result where english<60;

很简单对吧,有点像编程语言中的if语句

2.2.2 语文成绩在 [80, 90] 分的同学及语文成绩

语文成绩在 [80, 90] 分的同学及语文成绩:

我们要拿到的列是姓名+语文成绩,要求是语文成绩在[80, 90] 之间
select name,chinese from exam_result where chinese>=80 and chinese<=90;

当然,处理这种方式,还可以用between
select name,chinese from exam_result where chinese between 80 and 90;

2.2.3 数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩

查找数学成绩是 58 或者 59 或者 98 或者 99 分的同学及数学成绩:

查询的列是姓名+数学成绩,要是数学成绩是58/59/98/99
那就应该用逻辑运算符中的or了:
select name,math from exam_result where math=58 or math=59 or math=98 or math=99;

第二种方法:

还可以用in

select name,math from exam_result where math in (58,59,98,99);

2.2.4 姓孙的同学/孙某同学(模糊匹配)

查询姓孙的同学:

那就有一个问题,姓孙,那名字有可能是两个字,或者三个字,甚至更多。
那怎么找呢?
🆗,like模糊匹配就可以上场了

% 表示任意多个(包括 0 个)任意字符;_ 表示任意一个字符
那要找姓孙的人,孙字后面可能不止一个字,所以就应该用%
select name from exam_result where name like '孙%';
查询名字要从name列查询,所以就是name liek '孙%'

查找孙某同学:

孙某,名字只有两个字,所以要用_,孙后面只有一个字
select name from exam_result where name like '孙_';

2.2.5 语文成绩高于英语成绩的同学

WHERE 条件中比较运算符两侧都是字段(我们上面是一个字段跟一个常数比)

select name,chinese,english from exam_result where chinese>english;

2.2.6 总分在 200 分以下的同学

查找总分在200分以下的同学:

那我们要先计算出所有同学的总分,这个我们上面讲过了,select后面可以跟表达式(where后面也可以)

那现在要查找总分<200的

这样肯定没问题。

那where后面可以用重命名之后的别名进行条件判断吗

这样:

不行,报错了

他说总分是一个未知列。怎么回事?

2.2.7 别名不能用在where子句中

那这里我们就要来讨论一个问题了:

select name,math+chinese+english 总分 from exam_result where 总分<200;
我们来分析一下这条SQL语句的执行顺序。

我们把它分为三个部分,那这三个部分的执行顺序是怎么样的呢?
首先from这部分肯定是最先的,先要知道从哪个表里做查询,做筛选。
那第二是谁呢?
🆗,是where子句。然后你要告诉我筛选的条件,我拿着这些条件去筛选。
所以,最后是select子句,告诉我要去哪些列进行筛选和查找。

所以,那就清楚为什么上面报错了:

因为WHERE子句在查询中是在SELECT子句之前执行的,因此无法识别在SELECT子句中定义的别名。

那我们在where语句中重命名可以吗?


不行,列别名只能在 SELECT 子句中定义,不能在 WHERE 子句中定义,同时where子句中也不能使用别名。

2.2.8 语文成绩 > 80 并且不姓孙的同学(AND + NOT)

查询语文成绩 > 80 并且不姓孙的同学:

上面我们查询过姓孙的,like '孙%',那不姓孙的就是not like '孙%'
select name,chinese from exam_result where chinese>80 and name not like '孙%';

2.2.9 孙某同学,否则要求总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80

看起来有点复杂,这里的否则怎么理解:

就是说要么是孙某同学(姓孙,且名字是俩字),如果不是孙某,那么就要求其总成绩 > 200 并且 语文成绩 < 数学成绩 并且 英语成绩 > 80
所以是或的关系,那就要用or
select name,chinese,math,english,chinese+math+english total from exam_result where name like '孙_' or( chinese+math+english>200 and chinese<math and english>80);

就可以了,孙某同学直接选,不是孙某的,成绩符合条件的只有猪悟能。

2.2.10 NULL 的查询

讲这个的话,我们先来新建一张表,插入一些数据


现在我们想查询一下name为null的记录:


select * from test where name is null;

然后我来插入一条name为空串的记录

我们之前的文章说过,null和空串是不一样的。
所以现在再查name为null的,肯定没有空串这一行

查名字为''的,可以查到这条

查name不为空的

是包含这一行的,因为''不是null

如果用=查询的话,=是null不安全的,<=>是null安全的:

2.3 结果排序------order by子句

语法:

sql 复制代码
-- ASC 为升序(从小到大)
-- DESC 为降序(从大到小)
-- 默认为 ASC
SELECT ... FROM table_name [WHERE ...]
ORDER BY column [ASC|DESC], [...];

注意:没有 ORDER BY 子句的查询,返回的顺序是未定义的,永远不要依赖这个顺序

案例(下面还来看我们的exam_result表):

2.3.1 同学姓名及其数学成绩,按数学成绩升序/降序显示

查询同学及数学成绩,按数学成绩升序显示:

非常简单,而且order by默认就是升序。
使用默认行为:
select name,math from exam_result order by math;

指定asc(升序)
select name,math from exam_result order by math asc;

那降序呢?
select name,math from exam_result order by math desc;

2.3.2 null在排序中的地位

NULL 视为比任何值都小,升序出现在最上面

回到null的查询那张表:


首先按id升序:

NULL 视为比任何值都小,降序出现在最下面
按id降序

2.3.3 查询同学各门成绩,依次按 数学降序,英语升序,语文升序的方式显示

这里的依次是指:

第一优先级先按数学降序,如果数学成绩相等,此时按英语成绩升序,如果数学英语都相等,则按语文升序。
多字段排序,排序优先级随书写顺序
select name,chinese,math,english from exam_result order by math desc,english asc,chinese asc;

2.3.4 查询同学及总分,总分升序排序

查询同学及总分,总分升序排序:

select name,chinese+english+math as total from exam_result order by chinese+english+math;(默认升序)

2.3.5 order by 子句中可以使用别名

那现在有一个问题,之前我们讲了where子句中不可以用别名,那order by子句可以吗?

select name,chinese+english+math as total from exam_result order by total;

没问题。

为什么可以?

知道where子句里面为什么不能用,其实这里为什么能我们就应该知道了。
WHERE子句在查询中是在SELECT子句之前执行的,因此无法识别在SELECT子句中定义的别名。
所以,order by可以,肯定就是ORDER BY在SELECT之后执行,所以SELECT子句中定义的别名就能在order by中使用。

为什么ORDER BY在SELECT之后执行,简单分析一下它的合理性:

select 指明了要查找的列是哪些,然后order by只对这些指定的列进行排序,这当然很合理。
那先对所以数据排序,然后再从中拿到我想要的列可以嘛,当然也可以。但是是不是可能对很多不需要的列也进行了排序,那就做了很多无用功。
所以,这两种方式采取了前者。

2.3.6 查询姓孙的同学或者姓曹的同学的数学成绩,结果按数学成绩降序

select name,math from exam_result where name like '孙%' or name like '曹%' order by math desc;

在目前我们学过的这些子句中,SQL执行的顺序是:

FROM
WHERE
SELECT
ORDER BY

2.4 结果分页

语法:

sql 复制代码
-- 起始下标为 0(第一行的偏移量是 0)
-- 从 s 开始,筛选 n 条结果
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT s, n
-- 从 0 开始,筛选 n 条结果
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT n;
;
-- 从 s 开始,筛选 n 条结果,比第一种用法更明确,建议使用
SELECT ... FROM table_name [WHERE ...] [ORDER BY ...] LIMIT n OFFSET s;

举例:


这是查看表中所有的记录。
但现在我只想查看表中的前5行(如果表中记录非常多,我想查看前5行,使用上面的方式就非常不方便,你可能要自己往上翻)
select * from exam_result limit 5;
他就默认从第一行(偏移量为0),往后显式5条数据
我们不指定,默认从第一行开始(偏移量为0)。当然我们也可以自己指定:
select * from exam_result limit 2,3;

从偏移量为2的行开始(即第三行,第一行下标为0,第三行下标为2),往后显示3行,所以就是3,4,5行。
第二种写法:
select * from exam_result limit 3 offset 2;

显式指明起始偏移量和limit值(读取的行数),比第一种用法更明确,建议使用。

这样做有什么用呢?

LIMIT 子句的主要用途 之一就是实现结果的分页显示 。通过指定偏移量(OFFSET)和要返回的行数(LIMIT),可以每次只检索结果集中的一部分数据,从而实现在Web应用程序或其他客户端中分页显示数据。

例如,在一个Web应用中,用户查看一个产品列表,每页显示10个产品。当用户请求第1页时,查询使用LIMIT 10(或LIMIT 0,10);请求第2页时,查询使用LIMIT 10,10(跳过前10行,再取10行);请求第3页时,使用LIMIT 20,10,依此类推。

再比如我们上面对成绩排序,排完序我们使用limit就可以实现比如拿到第一名,拿到5~10名这样的操作。

加上limit后,SQL的执行顺序

FROM
WHERE
SELECT
ORDER BY
LIMIT

相关推荐
zhengfei6112 小时前
sqligo - 轻松检测和利用 SQL 注入漏洞
数据库·sql
没有bug.的程序员2 小时前
Spring Cloud Gateway:API网关限流与熔断实战
java·开发语言·数据库·spring boot·gateway·api·springcloud
OnYoung2 小时前
用Python实现自动化的Web测试(Selenium)
jvm·数据库·python
偷学技术的梁胖胖yo2 小时前
Shell脚本中连接数据库查询数据报错 “No such file or directory“以及函数传参数组
linux·mysql·shell
(;_;)(╥ω╥`)2 小时前
深入剖析Kafka(二)
数据库·kafka·linq
_F_y2 小时前
MySQL表的内连和外连
android·数据库·mysql
a努力。10 小时前
国家电网Java面试被问:混沌工程在分布式系统中的应用
java·开发语言·数据库·git·mysql·面试·职场和发展
li_wen0110 小时前
文件系统(八):Linux JFFS2文件系统工作原理、优势与局限
大数据·linux·数据库·文件系统·jffs2
wWYy.11 小时前
详解redis(16):缓存击穿
数据库·redis·缓存