【MYSQL】基本查询(表的增删查改)--详解

表的增删改查
CRUD : Create(创建), Retrieve(读取),Update(更新),Delete(删除)

给大家补充小知识点:(后面还会持续更新的)

**mysql不区分大小写,都可以(**针对 SQL 关键字和数据库名、表名、字段名)

mysql不支持+=;

select支持数学运算

一.Create

语法:

复制代码
INSERT [INTO] table_name [(column [, column ...])] VALUES (value_list) [, (value_list)] ...
部分 含义 是否必须
INSERT [INTO] 插入数据的关键字,INTO 可省略 必须
table_name 要插入数据的表名 必须
[(column [, column ...])] 指定要插入的列名,可省略(省略=全列插入) 可选
VALUES 表示后面跟要插入的值 必须
(value_list) 一组值,对应前面指定的列 必须
[, (value_list)] ... 可以跟多组值,表示一次插入多行 可选

全列插入 时,VALUES 左侧不指定列名,右侧必须为表中所有列 按定义顺序提供对应值,数量和类型必须完全匹配;按列插入 时,VALUES 左侧明确列出所需列名,右侧只需为这些列提供对应值,其余列自动使用默认值或 NULL。两者核心区别在于:是否显式指定列及值的数量是否与表结构一致。

创建一张学生表


1-1单行数据 + 全列插入

  • 插入两条记录,value_list 数量必须和定义表的列的数量及顺序一致
  • 注意,这里在插入的时候,也可以不用指定id(当然,那时候就需要明确插入数据到那些列了),那么mysql会使用默认的值进行自增

1-2多行数据 + 指定列插入

插入两条记录,value_list 数量必须和指定列数量及顺序一致

我前面还补充了几个都是用的单行插入但是不影响讲解


1-3插入否则更新

由于 主键 或者 唯一键 对应的值已经存在而导致插入失败

可以选择性的进行同步更新操作 语法:

INSERT ... ON DUPLICATE KEY UPDATE

column = value [, column = value] ...

之前还插入了一些为了方便我后续进行其他的操作:

INSERT ... ON DUPLICATE KEY UPDATE是一种有条件的插入或更新操作:

  • 当插入的数据没有与表中现有的主键或唯一键发生冲突时,就执行普通的插入操作。

  • 当插入的数据与主键或唯一键发生冲突 (即该键的值已存在)时,则不插入新记录,而是更新该冲突行中指定的列

示例:

sql 复制代码
INSERT INTO students (id, name, score) VALUES (1, '张三', 90)
ON DUPLICATE KEY UPDATE name = VALUES(name), score = VALUES(score);
  • VALUES(列名) 表示本次要插入的新值。

  • 可以同时更新多个列,用逗号分隔。

  • 0 row affected :表中已存在冲突数据(主键或唯一键重复),且冲突行的当前值与 UPDATE 子句中指定的新值完全相同,因此无需实际修改,故影响行数为 0。

  • 1 row affected:表中没有冲突数据,执行的是纯插入操作,因此影响行数为 1。

  • 2 row affected :表中存在冲突数据,且冲突行的原值与 UPDATE 子句中的新值不同,因此先删除旧行(1 行影响),再插入新行(1 行影响),总共影响 2 行。

简单记忆:0 表示有冲突但值未变,1 表示无冲突直接插入,2 表示有冲突且值被更新。

通过 MySQL 函数获取受到影响的数据行数

返回值 场景
-1 执行的是 SELECT 等非修改语句
0 有冲突,但新旧值相同,无实际修改
1 无冲突,执行插入
2 有冲突,且执行了更新

尝试插入一条 (id=13, sn=132, name='xuyou', qq='1111111') 的记录,但如果主键 id=13 已经存在,就改为更新这条已存在记录的 snnameqq 字段为指定值。


执行结果 2 rows affected 的含义

2 rows affected 表示:发生了主键冲突,且执行了更新操作

INSERT ... ON DUPLICATE KEY UPDATE 中:

  • 1 row affected--> 没有冲突,执行插入

  • 2 rows affected -->有冲突,执行更新(MySQL 内部是先删旧行再插新行的逻辑)

尝试插入一条 id=13, sn=132, name='xuyou', qq='1111111' 的记录,但如果 id=13 已经存在(主键冲突),则改为更新这条记录,将它的 snnameqq 分别设为指定的值。


Query OK, 1 row affected 表示:

  • 没有发生冲突id=13 原本不存在)

  • 执行的是 插入 操作

  • 所以新行 (13,132,'xuyou','1111111') 被成功插入


|---------------------------------------------|--------------------------------------------------|
| ALUES (13,132,'xuyou','1111111') | 要插入的数据:id=13, sn=132, name='xuyou', qq='1111111' |
| ON DUPLICATE KEY UPDATE | 如果主键(id)或唯一键冲突,就执行更新 |
| sn = 132, name = 'xuyou', qq = '11111111' | 冲突时,把这些列更新成新值 |

  • ON DUPLICATE KEY UPDATE先删除再插入,不是"比较新旧值是否相同"
  • 这就是"指定的值"的意义:冲突时以 UPDATE 里写的为准,而不是插入时的值 。(指定的值 "指的是 ON DUPLICATE KEY UPDATE 后面 等号右边你手动写的那个值

1-4替换

  • 主键 或者 唯一键 没有冲突,则直接插入;
  • 主键 或者 唯一键 如果冲突,则删除后再插入
rows affected 含义
1 无冲突,插入新行
2 有冲突,执行了删除 + 插入(数据可能变也可能不变,但 MySQL 物理上做了删插)
0 有冲突,且 新旧数据完全相同,MySQL 跳过了实际修改

三次执行结果对应的"替换情况"

第一次(1 row affected)

  • 没有冲突

  • 没有发生任何替换(插入新行)

第二次(2 rows affected)

  • 冲突发生

  • 发生了替换

    id=18 这一行的 sn / name / qq 替换成 UPDATE 里指定的值

  • 虽然插入值和旧值可能不同,但 MySQL 执行了物理删插

第三次(0 rows affected)

  • 冲突发生

  • 没有发生有效替换

    因为 UPDATE 里的值和当前行已经一模一样,MySQL 认为"无需替换"


REPLACE 的区别(容易混淆的点)

语句 是否删除整行 是否可能丢列 自增行为
REPLACE 会丢未指定的列 一定会新增自增 ID
INSERT ... ON DUPLICATE KEY UPDATE 否(逻辑是更新) 只改指定列 不一定会新增自增 ID(除非物理删插)
  • 1 row affected:表中没有冲突数据,数据被插入
  • 2 row affected:表中有冲突数据,删除重新插入

二.Retrieve

语法:

SELECT[DISTINCT] {* | {column [, column] ...}[FROM table_name][WHERE ...][ORDER BY column [ASC | DESC], ...]LIMIT ...

部分 含义 是否必须
SELECT 查询关键字 必须
[DISTINCT] 去重,去掉结果集中重复的行 可选
`{* column [, column ...]}` 选择哪些列:* 表示所有列,或列出指定列名
FROM table_name 从哪张表查 必须
[WHERE condition] 筛选条件,只返回满足条件的行 可选
[ORDER BY ...] 排序,按指定列升序(ASC)或降序(DESC)排列 可选
[LIMIT count] 限制返回的行数 可选

2-1select列

1)全列查询

**通常情况下不建议使用 * 进行全列查询

  1. 查询的列越多,意味着需要传输的数据量越大;
  2. 可能会影响到索引的使用。(索引待后面课程讲解)**

2)指定列查询

指定列的顺序不需要按定义表的顺序来


3)查询字段为表达式

表达式不包含字段

  • 表达式不包含字段
  • 表达式包含一个字段
  • 表达式包含对个字段

4)为查询结果指定别名

语法:

SELECT column [AS] alias_name [...] FROM table_name;

5)结果去重

98 分重复了


2-2WHERE 条件

比较运算符:

在 MySQL 中,= 用于判断两个值是否相等,但不能用来判断 NULL ,因为 NULL = NULL 的结果不是 TRUE,而是 NULL,不会匹配任何行。判断 NULL 必须使用 IS NULLIS NOT NULL。此外,NULL 表示"未知值",与数字 0 或空字符串 '' 完全不同,不要混用。

逻辑运算符:


练习

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

基本比较

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

使用 AND 进行条件连接

使用 BETWEEN ... AND ... 条件

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

使用 OR 进行条件连接

使用 IN 条件

姓孙的同学 及 孙某同学

% 匹配任意多个(包括 0 个)任意字符

_ 匹配严格的一个任意字符

语文成绩好于英语成绩的同学

WHERE 条件中比较运算符两侧都是字段

总分在 200 分以下的同学

WHERE 条件中使用表达式
别名不能用在 WHERE 条件中

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

AND 与 NOT 的使用

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

综合性查询

NULL 的查询

查询 qq 号已知的同学姓名

NULL 和 NULL 的比较,= 和 <=> 的区别


三.结果排序

练习:

语法:
-- ASC 为升序(从小到大)
-- DESC 为降序(从大到小)
-- 默认为 ASC

SELECT ... FROM table_name [WHERE ...]
ORDER BY column [ASC|DESC], [...];

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

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

我验证了颠倒一下啥感觉

同学及 qq 号,按 qq 号排序显示

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

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

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

多字段排序,排序优先级随书写顺序

查询同学及总分,由高到低

ORDER BY 中可以使用表达式

ORDER BY 子句中可以使用列别名

查询姓孙的同学或者姓曹的同学数学成绩,结果按数学成绩由高到低显示

结合 WHERE 子句 和 ORDER BY 子句

筛选分页结果

语法:

  • 起始下标为 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;

  • 建议:对未知表进行查询时,最好加一条 LIMIT 1,避免因为表中数据过大,查询全表数据导致数据库卡死 按 id 进行分页,每页 3 条记录,分别显示 第 1、2、3 页

第 3 页,如果结果不足 3 个,不会有影响

LIMIT 的本质功能是 控制结果显示的数量与起始位置 ,而不是条件筛选。

它的执行顺序非常靠后:必须先有数据(经过 WHERE 筛选、JOIN 关联、ORDER BY 排序等完整的数据准备阶段) ,最后才由 LIMIT 决定"从第几行开始显示、显示多少行"。

也就是说,LIMIT 不参与"哪些数据要被拿出来"的判断,只参与"拿出来的数据怎么展示"的最终控制。

因此,LIMIT 不能理解为"条件过滤",而应理解为 结果集的截取与分页展示工具


四.Update

语法:

UPDATE table_name SET column = expr [, column = expr ...]

WHERE ...\] \[ORDER BY ...\] \[LIMIT ...

对查询到的结果进行列值更新

将孙悟空同学的数学成绩变更为 80 分

更新值为具体值,查看原数据

数据更新,查看更新后数据

将曹孟德同学的数学成绩变更为 60 分,语文成绩变更为 70 分

一次更新多个列

查看更新后数据

将总成绩倒数前三的 3 位同学的数学成绩加上 30 分

更新值为原值基础上变更

查看原数据

别名可以在ORDER BY中使用

数据更新,不支持 math += 30 这种语法(更新后)

查看更新后数据

思考:这里还可以按总分升序排序取前 3 个么?

按总成绩排序后查询结果

将所有同学的语文成绩更新为原来的 2 倍

注意:更新全表的语句慎用!(没有 WHERE 子句,则更新全表)

查看原数据

数据更新,查看更新后数据


五. 删除数据

删除数据

语法:

DELETE FROM table_name [WHERE ...] [ORDER BY ...] [LIMIT ...]

删除孙悟空同学的考试成绩

查看原数据

删除数据

查看删除结果

删除整张表数据

注意:删除整表操作要慎用!

准备测试表

插入测试数据

查看测试数据

删除整表数据

查看删除结果

再插入一条数据,自增 id 在原值上增长

查看数据

查看表结构,会有 AUTO_INCREMENT=n 项

截断表

语法:

TRUNCATE [TABLE] table_name

截断整表数据,影响行数是 0,所以实际上没有对数据真正操作

注意:这个操作慎用

  1. 只能对整表操作,不能像 DELETE 一样针对部分数据操作;

  2. 实际上 MySQL 不对数据操作,所以比 DELETE 更快,但是TRUNCATE在删除数据的时候,并不经过真正的事

物,所以无法回滚

  1. 会重置 AUTO_INCREMENT 项

准备测试表

插入测试数据

查看测试数据

截断整表数据,注意影响行数是 0,所以实际上没有对数据真正操作

查看删除结果

再插入一条数据,自增 id 在重新增长

查看数据

查看表结构,会有 AUTO_INCREMENT=2 项

这张表叫 for_truncate,有一个自增主键 id,下一个自增值是 2。

使用 utf8mb4 字符集,不区分大小写和重音的排序规则,存储引擎是 InnoDB。

AUTO_INCREMENT=2

  • 表示下一个自增 id 的值是 2

  • 说明表中已经有一条记录(id=1)

  • 或者曾经有过 id=1 的记录(后来被删除,但自增不会回退)

DELETETRUNCATE 的区别

两者都可以清空表中的数据,但行为上有本质区别。

对自增计数器的影响: DELETE FROM 不会重置自增计数器,下次插入时 id 会从之前的值继续递增;而 TRUNCATE 会重置自增计数器,下次插入时 id 从 1 开始。

事务与回滚: DELETE FROM 支持事务,每行删除都在事务中执行,可以通过 ROLLBACK 回滚恢复数据;TRUNCATE 不支持事务,不经过事务处理,一旦执行就无法回滚,数据永久丢失。因此 TRUNCATE 操作必须慎用。

操作粒度: DELETE FROM 可以带 WHERE 条件,只删除部分数据;TRUNCATE 只能整表清空,不能加任何条件。

执行效率: DELETE FROM 是逐行删除,并记录每行的日志,所以速度较慢;TRUNCATE 本质上是直接释放数据页,不逐行操作,也不记录每行的日志,所以速度远快于 DELETE


六.插入查询结果

语法:

INSERT INTO table_name [(column [, column ...])] SELECT ...

删除表中的的重复复记录,重复的数据只能有一份

创建原数据表

插入测试数据

错误思路:直接用distinct

正确思路

创建一张空表 no_duplicate_table,结构和 duplicate_table 一样

将 duplicate_table 的去重数据插入到 no_duplicate_table

通过重命名表,实现原子的去重操作

查看最终结果

为什么最后是通过 rename 方式进行的?

具体过程是:

  1. 先创建一个临时表(或新表)。

  2. 在临时表中完成所有的数据清洗、去重、合并等操作(这个过程可能很慢,但它不影响线上业务)。

  3. 确认临时表中的数据已经准备就绪、完全正确后,再通过一条 RENAME TABLE 语句,原子地将原表替换成新表

RENAME TABLE 的本质是:把"复杂的数据准备过程"和"最终生效"分开。准备阶段在幕后偷偷做,最后通过一条原子指令瞬间切换,做到业务无感知、操作可回滚。


一.当前在 bash 看到了文件

说明你已经在 MySQL 的数据目录里,比如:

复制代码
cd /var/lib/mysql/test_db/
ls -l

去重操作不能在 bash 里做,必须回到 MySQL 命令行。


二、回到 MySQL 命令行

复制代码
mysql -u root -p

输入密码,然后进入对应数据库:

复制代码
USE test_db; 

三、查看当前有哪些表

复制代码
SHOW TABLES;

应该能看到:

  • duplicate_table(原表,有重复数据)

  • no_duplicate_table(临时空表,你已经创建好了)


四、向临时表插入去重数据

sql 复制代码
INSERT INTO no_duplicate_table
SELECT * FROM duplicate_table
GROUP BY name;

这里假设按 name 去重。如果你想按其他字段,把 name 换成你的字段名。


五、原子替换表名

sql 复制代码
RENAME TABLE duplicate_table TO duplicate_table_old,
             no_duplicate_table TO duplicate_table;

这一步执行完:

  • 新表 duplicate_table 上线(无重复数据)

  • 原表被备份为 duplicate_table_old


六、验证结果

sql 复制代码
SELECT * FROM duplicate_table;

确认数据正确后,删除备份表:

sql 复制代码
DROP TABLE duplicate_table_old

七. 聚合函数

练习:

统计班级共有多少同学

使用 * 做统计,不受 NULL 影响

使用表达式做统计

统计本次考试的数学成绩分数个数

COUNT(math) 统计的是全部成绩

COUNT(DISTINCT math) 统计的是去重成绩数量

distinct 在括号内,因为是要对 math 去重,而不是对 count() 的结果去重。

NULL 不会计入结果。

统计数学成绩总分

不及格 < 60 的总分,没有结果,返回 NULL,有直接返回成绩

统计数学平均成绩

统计平均总分

返回英语最高分

返回 > 70 分以上的数学最低分


8.group by子句的使用

在select中使用group by 子句可以对指定列进行分组查询

select column1, column2, .. from table group by column;

目的:为了进行分组 后,方便进行聚合统计

准备工作,创建一个雇员信息表(来自oracle 9i的经典测试表)

EMP员工表

DEPT部门表

SALGRADE工资等级表

1. DEPT(部门表)

2. EMP(员工表)

3. SALGRADE(工资等级表)

数据插入

1.如何显示每个部门的平均工资和最高工资

2.如何显示每个部门的平均工资和最高工资

分组(GROUP BY)的本质

GROUP BY 指定列名后,实际分组是以该列中不同取值的行来划分的。

分组后,同一组内的该列值一定是相同的,这意味着该列可以被"聚合压缩"------即多个行在分组后合并为一个组,组内的其他列需要通过聚合函数(如 SUMAVGMAXMINCOUNT)来处理。

理解分组的一种方式: 分组就是把一张表按照指定的条件,在逻辑上拆分成多个子表,每个子表对应一个组。然后针对每个子表单独进行聚合统计。

例如:按部门编号分组,就是把员工表拆成"10号部门子表"、"20号部门子表"、"30号部门子表"等,然后分别统计每个子表的平均工资、最高工资等。

3.显示每个部门的每种岗位的平均工资和最低工资

4.显示平均工资低于2000的部门和它的平均工资

统计各个部门的平均工资

having和group by配合使用,对group by结果进行过滤(having经常和group by搭配使用,作用是对分组进行筛选,作用有些像where)

5.having VS where 的区别与执行顺序是什么的。

都能够做条件筛选,这是它们的共性。

但它们是完全不同的条件筛选,它们的条件筛选的阶段是不同的。

WHERE 是在分组之前对原始行进行筛选,筛完再分组;HAVING 是在分组之后对聚合结果进行筛选。所以 WHERE 里不能用 AVGSUM 这类聚合函数,但 HAVING 里可以。执行顺序是:FROMWHEREGROUP BYHAVING。能用 WHERE 提前筛掉数据,就尽量别等分组后靠 HAVING 再筛,这样效率更高。

理解 MySQL 中的"表"

不要单纯地认为,只有在磁盘上将表结构导入到 MySQL、真实存在的表才叫表。

在 MySQL 中,一切查询结果都是逻辑上的表

  • 中间筛选出来的结果集,是表

  • 最终输出的结果,也是表

  • 子查询返回的结果,还是表

  • 甚至一个聚合统计后的输出,同样是表

只要你能够处理好单张表的增删改查(CURD),所有的 SQL 场景都可以用统一的方式去理解和操作。

因为无论是多表查询、子查询、分组统计还是聚合计算,本质上都是在"逻辑表"上做操作------只是这个表可能是临时的、筛选过的、聚合后的,但它依然遵循单表操作的基本规则。

【细节补充】

SQL查询中各个关键字的执行先后顺序 from > on> join > where > group by > with > having > select> distinct > order by > limit

相关推荐
爱喝水的鱼丶18 小时前
SAP-ABAP:变量、常量、结构与内表声明(10篇博客合集) 第八篇:复杂业务场景下的声明组合:结构嵌套内表、内表包含结构的实现方法
运维·数据库·学习·算法·sap·abap
这个DBA有点耶18 小时前
集中式 vs 分布式:2026数据库选型决策树
数据库·分布式·决策树
鸽芷咕18 小时前
KingbaseES系统视图与Hints调优:从诊断到性能优化的进阶之路
数据库·oracle·性能优化
Pocker_Spades_A18 小时前
没公网IP怎么远程连数据库?PostgreSQL + cpolar,在任何网络环境下都能连上
网络·数据库·tcp/ip
数据与后端架构提升之路18 小时前
RAG 实战指南:深入浅出向量数据库 Milvus
数据库·milvus
风间琉璃"""18 小时前
Reverse5
数据库
一只fish18 小时前
Oracle官方文档翻译《Database Concepts 26ai》第11章-服务器端编程
数据库·oracle
一只fish18 小时前
Oracle官方文档翻译《Database Concepts 26ai》第13章-事务
数据库·oracle
. . . . .18 小时前
mysql常用SQL
数据库·sql·mysql