1. 增删改查:为什么人人都会,却总在项目里翻车
我做过不少次技术面试,有一个很深的感触:几乎所有候选人都会在简历上写“熟练掌握MySQL增删改查”。可真到上手写SQL的时候,能把一条UPDATE写对的人,十个里未必有三个。这不是夸张——我就亲眼见过有人因为忘了加WHERE条件,一条UPDATE把整个表的某个字段全部改成了同一个值,那天下午整个办公室都在加班恢复数据。
增删改查这四个字说起来太轻巧了,好像就是INSERT、SELECT、UPDATE、DELETE四句话的事。但真正落到实际业务里,每一类操作都有大量的细节藏着:事务怎么开、索引怎么走、锁怎么加、批量操作怎么控制粒度、误操作怎么预防。这篇文章我不打算给你抄一遍文档,那东西官方手册写得比我清楚。我想做的是把手头的经验捋一遍,从一个实际项目的角度,把这四类操作讲透,让你看完之后写出来的SQL像干过几年活的人写的,而不是像刚从课本上抄下来的。
适合看这篇的人,我大概分三类:一类是刚入门、准备把增删改查从“会用”变成“用得好”的初学者;一类是转行过来、对MySQL只停留在Navicat里点点点的朋友;还有一类是做全栈开发、需要在业务代码里写大量数据操作的人。这篇文章里的经验和坑,都是我实际踩过的,你照着来,能少走不少弯路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动手之前的准备:MySQL环境与学习库的设计
2.1 本地装MySQL还是用Docker,怎么选
很多人一上来就问“MySQL怎么安装”,但我建议你先想清楚安装场景。
如果你只是为了学习增删改查,我强烈推荐用Docker起一个MySQL实例。原因很简单:干净、可摧毁、可重建。你在学习过程中难免会做一些破坏性操作,比如删库、改错数据、把表结构搞乱。Docker容器两秒钟就能重新拉起来,完全不影响你继续练手。我自己当年学的时候就是在本地拿虚拟机折腾,一个配置改错了,卸载装、装完卸载,浪费了大量时间。
bash复制# 拉取MySQL 8.0镜像并启动容器
docker run --name mysql-study \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:8.0
这条命令跑完,一个MySQL 8.0就起来了。如果你是Windows本地想装原生版本,去官网下载安装包就行,但记得选“Server only”组件,别把那些没用的Connector、Documentation全勾上——除非你明确知道自己需要。
注意:MySQL 8.0和5.7在不少行为上有差异,比如认证插件和窗口函数支持。如果是照着老教程学,遇到密码认证不通过,先在命令行执行
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';试试。
2.2 建一个像样的学习库:学生选课成绩系统
增删改查不是拿来背的,得有一张合理的表,才能感受到每个语句存在的意义。我拿“学生-课程-成绩”这个经典场景来建表,因为这也是面试题里的常客,热搜词里也有“学生课程成绩信息实体表设计mysql”,说明这是大家普遍关心的场景。
先分析业务需求:一个学生可以选择多门课程,一门课程可以被多个学生选择,学生选了课之后会产生一个成绩。这个关系对应于数据库里最典型的关联关系:学生表和课程表是基础数据,中间还需要一张选课成绩表来记录“某个学生选了某门课、考了多少分”。
sql复制-- 学生表
CREATE TABLE `student` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '学生ID,自增主键',
`student_no` VARCHAR(20) NOT NULL COMMENT '学号',
`name` VARCHAR(50) NOT NULL COMMENT '姓名',
`gender` TINYINT DEFAULT '0' COMMENT '性别:0未知,1男,2女',
`birth_date` DATE DEFAULT NULL COMMENT '出生日期',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_student_no` (`student_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
-- 课程表
CREATE TABLE `course` (
`id` INT NOT NULL AUTO_INCREMENT,
`course_code` VARCHAR(20) NOT NULL COMMENT '课程编号',
`course_name` VARCHAR(100) NOT NULL COMMENT '课程名称',
`credit` DECIMAL(3,1) DEFAULT '2.0' COMMENT '学分',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_course_code` (`course_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程表';
-- 选课成绩表
CREATE TABLE `student_course` (
`id` INT NOT NULL AUTO_INCREMENT,
`student_id` INT NOT NULL COMMENT '学生ID',
`course_id` INT NOT NULL COMMENT '课程ID',
`score` DECIMAL(5,2) DEFAULT NULL COMMENT '成绩,NULL表示未考试',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_student_course` (`student_id`, `course_id`),
KEY `idx_course_id` (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='选课成绩表';
这里有几个设计细节值得注意:
- 主键用自增ID,但唯一业务标识用UNIQUE KEY。学号、课程编号这些真实的业务编码虽然唯一,但不适合做关联外键,因为有可能调整。你实际做项目时,主键尽量保持“无业务含义”,这样表结构才能稳定。
- 外键要不要建?在这三张表的设计里,我没有添加物理外键约束,只建了普通索引。在实际项目里我倾向于不建物理外键,原因后面会在“删除”那一章详细说。
- utf8mb4而不是utf8。utf8在MySQL里最多存3个字节,遇到emoji表情符就直接报错。从5.5以后官方推荐就是utf8mb4,现在新项目直接用utf8mb4,别问为什么,用就完了。
有了这三张表,后面的增删改查才有承载的土壤。你学任何一句SQL,都可以在这套业务逻辑里面找到对应场景,这就比打印一句SELECT 'hello'有意义多了。
