增删改查是数据库操作的基础,但不同层次的理解深度决定了 SQL 编写的质量和效率。本文以层次递进的方式,从最基础的语法,到执行逻辑、性能优化,再到高级注意事项,系统梳理 MySQL 中的 CRUD 核心知识。
一、基础操作
1.1 INSERT:插入数据
sql
-- 单行插入
INSERT INTO users (name, age) VALUES ('Alice', 25);
-- 多行插入
INSERT INTO users (name, age) VALUES ('Bob', 30), ('Carol', 28);
-- 插入查询结果
INSERT INTO archive_users SELECT * FROM users WHERE age > 60;
1.2 SELECT:查询数据
sql
-- 基本查询
SELECT name, age FROM users WHERE age > 18;
-- 排序与分页
SELECT * FROM users ORDER BY age DESC LIMIT 10, 20; -- 跳过10条取20条
-- 聚合与分组
SELECT department, AVG(salary) FROM employees GROUP BY department HAVING AVG(salary) > 5000;
1.3 UPDATE:更新数据
sql
-- 更新单行/多行
UPDATE users SET age = 26 WHERE name = 'Alice';
-- 批量更新(谨慎使用)
UPDATE users SET status = 1 WHERE age > 60;
1.4 DELETE:删除数据
sql
-- 条件删除
DELETE FROM users WHERE id = 123;
-- 清空全表
TRUNCATE TABLE temp_logs;
二、执行逻辑
2.1 SELECT 的逻辑执行顺序
书写顺序 ≠ 执行顺序。数据库引擎按以下顺序处理:
- FROM:确定表及连接方式
- WHERE:逐行过滤
- GROUP BY:分组
- HAVING:过滤分组
- SELECT:选择列、计算表达式
- ORDER BY:排序
- LIMIT:截取行数
这个顺序解释了为什么 WHERE 不能用 SELECT 中的别名,而 ORDER BY 可以。例如:
sql
-- 错误:WHERE 无法识别别名 total
SELECT salary * 12 AS total FROM emp WHERE total > 100000;
-- 正确:使用表达式或子查询
SELECT salary * 12 AS total FROM emp WHERE salary * 12 > 100000;
2.2 索引如何影响 CRUD
- SELECT :索引加速
WHERE、JOIN、ORDER BY、GROUP BY,且覆盖索引可避免回表。 - INSERT/UPDATE/DELETE:索引虽加速条件定位,但会带来维护成本。每次修改索引列都需要同步更新索引结构。
2.3 事务与锁
在 InnoDB 中:
- SELECT 默认不加锁(快照读),除非使用
FOR UPDATE或LOCK IN SHARE MODE。 - INSERT 产生插入意向锁,通常不阻塞其他插入。
- UPDATE/DELETE 对匹配行加排他锁,并可能加间隙锁,范围越大锁越多。
三、性能与优化
3.1 插入优化
- 批量插入 :
INSERT INTO ... VALUES (...), (...)比逐条插入快数倍。 - 关闭自动提交:将多条插入包在一个事务中,减少磁盘同步。
- 使用
LOAD DATA INFILE:适合大批量文本导入,效率最高。
3.2 查询优化
- 避免
SELECT *:只取需要的列,减少网络传输和内存开销。 - 索引列上避免函数或运算 :
WHERE YEAR(create_time) = 2023无法走索引,应改为范围查询。 - 联合索引最左前缀 :索引
(a, b, c)支持a、a,b、a,b,c的查询,不支持跳过a使用b。 - 覆盖索引 :查询的列都包含在索引中,无需回表,
EXPLAIN显示Using index。
3.3 更新与删除优化
- 使用索引定位 :
WHERE条件列有索引时,更新/删除只锁定少量行,否则可能锁全表。 - 分批操作:大范围更新/删除应拆分成多批,每批处理几千行,减少长事务和锁持有时间。
- 注意碎片 :频繁
DELETE后表空间不会释放,可定期OPTIMIZE TABLE整理碎片(低峰期执行)。
四、注意事项
4.1 INSERT 的主键冲突处理
-
INSERT IGNORE:跳过冲突行,适合批量导入时忽略重复。 -
ON DUPLICATE KEY UPDATE:存在则更新,不存在则插入,常用于幂等写入。sqlINSERT INTO daily_stats (date, pv) VALUES ('2023-01-01', 100) ON DUPLICATE KEY UPDATE pv = pv + VALUES(pv);
4.2 UPDATE 与 DELETE 的"危险"设计
- 不带
WHERE会作用于全表,这是 SQL 语法允许的,但极易误操作。建议在开发环境开启sql_safe_updates模式,禁止无条件的更新删除。 DELETE不会重置自增计数器,TRUNCATE会。
4.3 锁与死锁
- 更新/删除时,如果多个事务以不同顺序锁定多行,可能发生死锁。优化方式:尽量按相同顺序操作,缩短事务,合理设计索引减少锁范围。
- 间隙锁可能导致并发插入阻塞,在
REPEATABLE READ下尤其明显。评估业务是否需要使用READ COMMITTED隔离级别。
4.4 大数据量下的特殊手段
- 归档旧数据:定期将历史数据移出主表,减小主表体积。
- 分区表:按时间等维度分区,便于快速删除整个分区(
TRUNCATE PARTITION)。 - 异步批量删除:通过程序分批循环删除,避免长事务。
总结
掌握增删改查的层次结构:
- 基础层:能写出正确的 SQL。
- 逻辑层:理解语句执行顺序、索引和锁的影响。
- 优化层:针对性能瓶颈进行优化。
- 高级层:应对复杂场景和工程陷阱。