MySQL基础篇之表的增删查改(CRUD)细则

我们在 MySQL 基础篇中已经了解了库与表的定义操作,本篇详细讲解表数据的增删改查(CRUD),以及条件筛选、排序分页、聚合分组等核心查询能力,并梳理 SQL 执行的底层逻辑。


(一)增:INSERT 数据插入
1. 基础插入语法
复制代码
INSERT [INTO] 表名 [(字段1, 字段2, ...)]
VALUES (值1, 值2, ...), (值1, 值2, ...), ...;
  • 两种插入模式:
    • 全列插入:省略字段列表,VALUES 必须按表定义的字段顺序传入所有值
    • 指定列插入:明确写出字段名,VALUES 与字段一一对应,未指定字段使用默认值或 NULL
  • 支持单行插入多行批量插入;批量插入减少网络交互和 IO 次数,性能远高于逐行插入,推荐批量操作。

示例:

复制代码
-- 单行指定列插入
INSERT INTO test (ename) VALUES ('孙权');

-- 多行批量插入
INSERT INTO test (ename) VALUES ('刘备'), ('曹操'), ('关羽');
2. 主键 / 唯一键冲突的原子解决方案

插入数据时,如果表存在主键或唯一键,重复插入会直接报错。针对 "存在则更新、不存在则插入" 的业务场景,MySQL 提供两种原子方案,替代低效的 "先查询再插入" 试错逻辑。

方案 1:ON DUPLICATE KEY UPDATE(生产推荐)

作用 :插入时如果遇到主键 / 唯一键冲突,就执行UPDATE子句,更新冲突行的指定字段;如果无冲突,就正常插入。

复制代码
INSERT [INTO] 表名 [(字段列表)]
VALUES (值列表)
ON DUPLICATE KEY UPDATE 字段1 = 新值1, 字段2 = 新值2, ...;

示例:

复制代码
-- id是唯一键,插入'孙权'冲突时,将冲突行的ename更新为'孙权'
INSERT INTO test (id,ename) VALUES (1,'孙权'),(2,'刘备'),(3,'苏烈')
ON DUPLICATE KEY UPDATE ename = VALUES(ename);
 --- id发生冲突使用孙权/刘备/苏烈进行替换

关键说明:

  • 支持多行批量插入,每一行独立判断冲突、独立执行更新,不会互相影响
  • 可以用VALUES(字段名)引用本次插入的值,例如:ON DUPLICATE KEY UPDATE age = VALUES(age) + 1
  • 是原子操作,并发安全,性能远高于 "先查后插",是业务中处理 "upsert" 场景的主流方案
方案 2:REPLACE INTO

作用 :插入时如果遇到主键 / 唯一键冲突,先删除旧行,再插入新行;如果无冲突,直接插入。

复制代码
REPLACE [INTO] 表名 [(字段列表)]
VALUES (值列表), (值列表), ...;

示例:

复制代码
REPLACE INTO test (id, ename) VALUES (10, 'd');

关键说明:

  • 本质是「删除 + 插入」,未在语句中指定的字段,会被覆盖为表的默认值,会丢失原有数据
  • 会触发 DELETE 和 INSERT 触发器,自增主键会重新生成
  • 副作用较大,仅适合全字段覆盖的特殊场景,绝大多数场景推荐使用ON DUPLICATE KEY UPDATE

(二)查:SELECT 数据查询

查询是 SQL 中最核心、最复杂的部分,我们按照逻辑执行顺序逐层拆解。

1. 基础投影:列的选择
(1)全列查询
复制代码
SELECT * FROM 表名;

强烈不推荐生产环境使用 *

① 列越多,网络传输的数据量越大,查询性能下降

② 可能导致优化器无法选择合适的索引,出现索引失效

③ 表结构变更时,业务代码容易出现字段不兼容问题

(2)指定列查询
复制代码
SELECT 字段1, 字段2, ... FROM 表名;
(3)表达式与计算列

SELECT 后不仅可以写字段,还可以写任意合法的表达式(常量、算术运算、函数调用等),生成计算列。 示例:

复制代码
-- 10是常量表达式,作为计算列输出
SELECT id, 10 FROM test;

-- 字段运算 + 函数
SELECT id, salary * 12 AS annual_salary, UPPER(ename) FROM test;
(4)列别名

可以为字段或表达式设置别名,提升可读性,别名可用于后续的 ORDER BY 等子句。

复制代码
SELECT id AS student_id, ename AS student_name FROM test;
-- AS关键字可以省略
SELECT id student_id, ename student_name FROM test;
2. 结果去重:DISTINCT

对查询结果的整行去重,所有列的值都相同时,只保留一行。

复制代码
SELECT DISTINCT 字段1, 字段2, ... FROM 表名;
3. 条件筛选:WHERE

WHERE 子句在 FROM 之后执行,按条件过滤行,只有满足条件的行才会进入后续处理。

常用运算符与示例
运算符 说明 示例
>, >=, <, <= 大于、大于等于、小于、小于等于 SELECT id FROM test WHERE id > 10;
= 等于;不能判断 NULLNULL = NULL结果为 NULL SELECT * FROM test WHERE id = 1;
<=> NULL 安全等于;NULL <=> NULL返回 TRUE (1) SELECT * FROM test WHERE name <=> NULL;
!=, <> 不等于 SELECT * FROM test WHERE id != 5;
BETWEEN a AND b 范围匹配,闭区间[a,b] SELECT * FROM test WHERE id BETWEEN 1 AND 10;
IN (值1, 值2, ...) 匹配任意一个值 SELECT * FROM test WHERE id IN (1,3,5);
IS NULL 判断为 NULL SELECT * FROM test WHERE name IS NULL;
IS NOT NULL 判断不为 NULL SELECT * FROM test WHERE name IS NOT NULL;
LIKE 模糊匹配;%匹配任意个字符,_匹配单个字符 SELECT * FROM test WHERE ename LIKE '孙%';``SELECT * FROM test WHERE ename LIKE '孙_';
NOT LIKE 反向模糊匹配 SELECT * FROM test WHERE ename NOT LIKE '孙%';

注意:不要对数值类型字段使用 LIKE 模糊查询,会发生隐式类型转换,导致索引失效。

重要规则:WHERE 中不能使用 SELECT 别名

因为 SQL 逻辑执行顺序中,WHERESELECT 之前执行,WHERE 执行时还没有生成列别名,因此下面的语句会报错:

复制代码
-- 错误:WHERE无法识别student_id别名
SELECT id student_id FROM test WHERE student_id = 10;
4. 排序:ORDER BY

对 SELECT 输出的结果集按指定字段排序,在 SELECT 之后执行,因此可以使用 SELECT 的列别名。

复制代码
-- 升序(默认,可省略ASC)
SELECT id FROM test WHERE id LIKE '1%' ORDER BY id ASC;

-- 降序
SELECT id FROM test ORDER BY id DESC;
  • 支持多字段排序,例如ORDER BY age DESC, id ASC:先按 age 降序,age 相同按 id 升序
5. 分页:LIMIT

截取最终结果的指定行数,避免大数据量返回,是分页查询的核心语法。

复制代码
SELECT 字段 FROM 表名 
WHERE 条件 
ORDER BY 排序字段
LIMIT [偏移量,] 行数;
  • LIMIT N:取前 N 条
  • LIMIT S, N:从第 S 条开始(从 0 计数),取 N 条

示例:

复制代码
-- 取前5条
SELECT * FROM test LIMIT 5;

-- 从第10条开始,取5条(第3页,每页5条)
SELECT * FROM test ORDER BY id LIMIT 10, 5;

(三)改:UPDATE 数据更新
复制代码
UPDATE 表名 
SET 字段1 = 表达式1, 字段2 = 表达式2, ...
[WHERE 条件]
[ORDER BY 排序字段]
[LIMIT 行数];
  • 执行顺序:先 WHERE 筛选行,再 ORDER BY 排序,最后按 LIMIT 限制行数,执行更新
  • 注意 :UPDATE 的 LIMIT 仅支持LIMIT N(限制更新行数),不支持带偏移量的写法
  • 省略 WHERE 会更新全表所有数据,生产环境必须谨慎

示例:

复制代码
-- 将id>10的行,按id降序,更新前2行的id+1
UPDATE test SET id = id + 1 WHERE id > 10 ORDER BY id DESC LIMIT 2;

(四)删:DELETE 数据删除
复制代码
DELETE FROM 表名
[WHERE 条件]
[ORDER BY 排序字段]
[LIMIT 行数];
  • 执行顺序与 UPDATE 一致:WHERE 筛选 → ORDER BY 排序 → LIMIT 限制 → 执行删除
  • 同样,LIMIT 仅支持LIMIT N,不支持偏移量
  • 省略 WHERE 会删除全表所有行,但不会重置auto_increment自增计数器
  • DELETE 是 DML 语句,可通过事务回滚

示例:

复制代码
-- 删除id>10的前2行
DELETE FROM test WHERE id > 10 ORDER BY id DESC LIMIT 2;

(五)清空表:TRUNCATE
复制代码
TRUNCATE [TABLE] 表名;
  • 本质:DDL 语句,直接删除表并重建一个结构相同的空表
  • 特点:
    1. 执行速度极快,不逐行删除,直接释放表空间
    2. 重置auto_increment自增计数器
    3. 隐式提交事务,无法回滚
    4. 不会触发 DELETE 触发器
  • 对比:DELETE FROM 表名是逐行删除 DML,可回滚、不重置自增;TRUNCATE 是整表重建 DDL,不可回滚、重置自增。

(六)聚合函数与分组查询
1. 聚合函数

对一组行进行计算,返回单个结果,常用于统计分析。

函数 说明
COUNT([DISTINCT] 表达式) 返回匹配的行数;COUNT(*)统计所有行,COUNT(字段)忽略 NULL
SUM([DISTINCT] 表达式) 返回数值总和,非数值类型无意义
AVG([DISTINCT] 表达式) 返回数值平均值,非数值类型无意义
MAX([DISTINCT] 表达式) 返回最大值
MIN([DISTINCT] 表达式) 返回最小值
2. 分组:GROUP BY

将 WHERE 筛选后的结果,按指定字段分组,字段值相同的分为一组,配合聚合函数实现分组统计。

复制代码
SELECT 分组字段, 聚合函数, ...
FROM 表名
[WHERE 条件]
GROUP BY 分组字段1, 分组字段2, ...;

核心规则(标准 SQL / MySQL 默认模式) : SELECT 后面的字段,必须是分组字段,或者聚合函数;非分组字段不能直接出现在 SELECT 中。

复制代码
-- 错误:id不是分组字段,也不是聚合函数(开启ONLY_FULL_GROUP_BY时会报错)
SELECT id, COUNT(*) FROM test GROUP BY ename;

-- 正确:按ename分组,统计每组人数
SELECT ename, COUNT(*) AS num FROM test GROUP BY ename;

说明:MySQL 5.7 及以上默认开启ONLY_FULL_GROUP_BY模式,强制遵守上述规范;关闭模式下虽允许非分组列,但返回的行数据不确定,属于不规范写法,生产环境禁止使用。

3. 分组后筛选:HAVING

对 GROUP BY 分组后的结果进行条件过滤,必须配合 GROUP BY 使用。

  • WHERE:分组前筛选行,不能包含聚合函数
  • HAVING:分组后筛选分组,通常包含聚合函数

示例:

复制代码
-- 统计每个部门的平均工资,筛选出平均工资>8000的部门
SELECT dept, AVG(salary) AS avg_sal
FROM employee
GROUP BY dept
HAVING AVG(salary) > 8000;

(七)SQL 逻辑执行顺序总览

再次明确完整的执行顺序,这是理解所有 SQL 语法的核心:

执行顺序 子句 作用
1 FROM / JOIN 确定数据源,生成笛卡尔积,得到初始中间表
2 WHERE 按条件筛选行,过滤不符合的记录
3 GROUP BY 对筛选后的行进行分组
4 HAVING 对分组后的结果进行条件过滤
5 SELECT 投影列:选择字段、计算表达式、生成别名
6 DISTINCT 对结果集去重
7 ORDER BY 对最终结果集按指定列排序
8 LIMIT 截取最终返回的行数

三、知识拓展与补充

1. SELECT * 为什么会影响索引?B + 树会变吗?

B + 树索引本身不会因为查询语句改变。索引叶子节点只存储索引字段 + 主键,如果查询的字段都在索引中(覆盖索引),就可以直接从索引返回结果,无需回表。 使用SELECT *时,需要查询所有字段,通常必须回表访问聚簇索引,优化器评估成本后可能会放弃索引、选择全表扫描,导致性能下降。

2. INSERT 的其他实用写法
  • INSERT ... SELECT:从另一张表查询结果并插入,实现批量数据迁移

    复制代码
    INSERT INTO table2 (id, name) SELECT id, name FROM table1 WHERE id > 10;
  • INSERT IGNORE:插入冲突时直接忽略,不报错也不更新

    复制代码
    INSERT IGNORE INTO test (id) VALUES (1);
3. DELETE、TRUNCATE、DROP 的核心区别
操作 类型 作用 可回滚 重置自增 速度
DELETE DML 删除指定行 慢(逐行)
TRUNCATE DDL 清空整表,重建表结构
DROP DDL 删除整个表(结构 + 数据) - 最快
4. GROUP BY WITH ROLLUP

在分组统计的基础上,自动生成各级汇总行,适合报表统计场景:

复制代码
SELECT dept, job, COUNT(*) 
FROM employee 
GROUP BY dept, job WITH ROLLUP;
相关推荐
不剪发的Tony老师2 小时前
DuckDB教程:常用文本函数
数据库·sql·数据分析·duckdb
莫陌尛.11 小时前
Docker MySQL 删表卡死、执行超时(MDL锁阻塞)问题复盘与根治方案
mysql·docker·容器
毕业设计70314 小时前
(免费领源码) 基于微信小程序的预制菜商城的设计与实现25172-java、PHP、python、C#、小程序、大数据、单片机、网络工程等)
vue.js·python·mysql·微信小程序·pycharm·微信开发者工具·推荐算法
泡泡鱼(敲代码中)16 小时前
MySQL基础学习笔记:从数据模型到DDL全掌握
开发语言·数据库·笔记·学习·mysql
冰暮流星17 小时前
mysql之表子查询
数据库·mysql
风跟我说过她19 小时前
SQL 一键转经典 Chen 风格 ER 图:开源 CLI + 在线工具 + Agent Skill
数据库·python·sql·开源·开源软件
这个DBA有点耶19 小时前
MySQL大表DDL锁表锁到崩溃?Online DDL的3个关键参数和实战避坑
数据库·mysql·架构
兀兀术1 天前
windows下忘记pgsql密码
sql·pgsql
庆登登登1 天前
MySQL Workbench 鸿蒙 PC 适配全记录:从 GTK_X11 桌面程序到 ArkUI 原生数据库工作台
数据库·mysql·harmonyos