资讯动态

MySQL增删改查实战指南:从建表到事务处理的完整解析

发布时间:2026/9/13 15:36:15 来源:尧图企业网站定制
做MySQL开发这些年我见过太多人栽在增删改查这类“基本功”上。明明就是四个动作——插入、查询、更新、删除可我在帮别人排查问题的时候经常遇到忘写WHERE条件把整张表清空的、建表时把金额存成FLOAT导致对不上账的、还有字符集不统一导致中文全部变成“???”的。你说这些问题有多高级没有全是增删改查层面的疏忽。所以看到这个标题我第一反应是终于有人愿意静下心来把基础捋一遍了。这篇内容我会围绕MySQL 5.7环境把数据库、表、记录三个层级的所有增删改查操作完整过一遍。不管你是在做数据库课程设计还是刚入职需要快速上手公司老项目又或者准备面试前想系统梳理SQL语法这篇文章都适合你。我会从底层执行逻辑讲到具体语法再给出一套可以直接抄作业的学生选课系统案例最后把高频报错和排查思路整理成速查表。全程用我实际踩过的坑来说话保证比单纯看官方文档更有代入感。1. 增删改查到底在干什么先想清楚再动手1.1 数据库、表、记录三个层级的关系很多新手一开始就搞混了“库、表、记录”这三个概念导致执行SQL的时候总在问“我这条语句到底该操作谁”。用最简单的话说数据库是一个文件夹表是文件夹里的Excel文件记录是Excel文件里的一行行数据。你要打开Excel先得进到文件夹要改某一行数据先得定位到对应的Excel文件这就是“库→表→记录”的操作顺序。对应到MySQL命令上就非常清晰了。操作数据库用CREATE DATABASE、DROP DATABASE操作表结构用CREATE TABLE、ALTER TABLE、DROP TABLE操作记录用INSERT、SELECT、UPDATE、DELETE。这三个层级在权限管理上也是分开的一个用户可能只有某张表的查询权限没有删除权限这在企业级数据库里非常常见。在MySQL 5.7里还有一点要注意命令的大小写默认不敏感但表名和字段名的敏感度取决于操作系统。Linux环境下表名是区分大小写的Windows环境下不区分。这就导致同一个项目的SQL脚本在Windows开发环境跑得好好的部署到Linux服务器上就报“表不存在”。我建议从一开始就统一规范库名、表名、字段名全部小写多个单词用下划线分隔这是目前最主流的命名习惯。1.2 为什么我还在推荐MySQL 5.7做练习这里要先解释一下虽然MySQL 8.0已经发布好几年了但5.7依然是大量中小型项目、课程设计、老系统的首选。原因有三个第一5.7的稳定性和兼容性经过了市场充分验证网上随便一搜就是海量资料遇到问题基本都能找到解决方案第二很多云数据库RDS默认版本还是5.7公司用的可能也是5.7第三5.7和8.0在增删改查层面的语法差异极小把5.7练熟了切到8.0几乎无缝衔接。我自己的习惯是本地用Docker跑一个5.7的实例一条命令就能搞定docker run -d --name mysql57 -p 3306:3306 -e MYSQL_ROOT_PASSWORDroot123 mysql:5.7连接的时候用命令行工具、Navicat或者DataGrip都行。我这里特别提一句如果你的电脑是M系列芯片的Mac直接装Docker镜像是最省心的方式不用去处理原生安装包的各种权限问题。Windows用户直接下载安装包一路下一步也问题不大但记得安装时选UTF-8字符集避免后面乱码。1.3 CRUD命令背后MySQL替你做了多少事很多人以为执行一条SELECT语句数据库就是把数据捞出来那么简单。实际上MySQL内部有一套完整的执行流程连接器先校验你的身份和权限然后分析器做词法语法解析判断SQL写没写错接着优化器决定走哪个索引、用哪种关联顺序最后执行器才真正调用存储引擎接口去读写数据。就拿最经典的SELECT * FROM user WHERE age 18来说分析器会把“user”解析成表名、“age”解析成字段名优化器会判断是全表扫描还是走age字段上的索引执行器则一行一行去存储引擎拉取满足条件的数据。这个流程理解清楚了你就明白为什么有时候加个索引查询快几十倍为什么明明结果一样但写法不同执行效率天差地别。在5.7版本里默认存储引擎是InnoDB它支持事务、行级锁和外键约束。MyISAM虽然查询速度快但不支持事务崩溃恢复能力也差现在基本只在特定场景下才会用了。作为初学者你不需要纠结引擎选型默认InnoDB就对了我只在实际工作中遇到过需要把某张表改成MyISAM来快速导入数据的场景属于特例。2. 数据库与表结构操作建错了后面全是坑2.1 数据库级操作创建、修改、删除数据库级操作是增删改查的最外层语法本身很简单但有几个细节非常关键。创建数据库的标准写法是CREATE DATABASE IF NOT EXISTS student_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;IF NOT EXISTS是我强烈建议加上的因为MySQL报“database exists”虽然不影响现有数据但自动化脚本执行到这里会中断。真正重要的是指定字符集和排序规则utf8mb4是目前最通用的字符集能存下emoji表情兼容所有中文场景。utf8mb4_unicode_ci是基于Unicode的排序规则比utf8mb4_general_ci更准确。很多老项目用了utf8mb3也就是utf8遇到生僻字或者特殊符号就会出现“Incorrect string value”的报错原因就在这里。修改数据库的语句日常很少用主要就是改字符集ALTER DATABASE student_system CHARACTER SET utf8mb4;删除数据库的语句要格外慎重DROP DATABASE IF EXISTS student_system;这条命令执行后库里的所有表和数据会立刻消失没有任何回收站。我曾经见过有人在生产环境把一个几百万条数据的库删了幸好有当天凌晨的备份但光恢复就花了两个小时。删库之前务必备份或者先RENAME成一个带“_to_delete”后缀的名字确认业务不受影响再真删这套流程虽然保守但对保命很重要。2.2 建表的字段类型与约束选择建表是整个数据库设计的核心字段类型选错了后面应用层的坑一个接一个。MySQL 5.7里最常用的字段类型就几类整数类型里INT占4字节范围在-21亿到21亿之间超过这个范围就用BIGINT。我遇到过有人把订单ID设计成INT结果数据量大了以后直接超出范围上线两年就得改表结构。金额类字段一定要用DECIMAL比如DECIMAL(10,2)10表示总位数2表示小数位数它能精确存储不会出现FLOAT那种0.10.2不等于0.3的问题。这张表是我根据实际项目经验整理的核心字段选型场景推荐类型原因用户ID、订单IDBIGINT UNSIGNED避免超出INT范围年龄、数量INT / SMALLINT范围够用就行手机号VARCHAR(20)手机号是字符串不是数字金额DECIMAL(10,2)精确浮点避免精度丢失日期DATE / DATETIMEDATE存生日DATETIME存订单时间内容描述TEXT / VARCHAR(500)短文本用VARCHAR长文本用TEXT状态标志TINYINT0-255够用节省空间字段约束方面主键约束PRIMARY KEY保证唯一性和非空UNIQUE约束保证业务唯一比如手机号NOT NULL避免空值导致的统计问题DEFAULT提供默认值AUTO_INCREMENT配合主键实现自增。外键约束FOREIGN KEY能保证关联数据的完整性但在实际项目中很多团队会刻意不用外键改在应用层做逻辑校验原因是外键会影响插入和删除性能而且在高并发场景下容易引发锁竞争。如果你是在做课程设计建议加上外键文档里能多写一句“体现了数据的完整性与一致性”如果在企业实习先看项目现有约定跟着团队风格走。2.3 修改表结构的四个命令要分清ALTER TABLE这个命令下面有四个常见动作我每次看到有人搞混就头大。ADD COLUMN是新增字段通常带AFTER指定位置ALTER TABLE student ADD COLUMN phone VARCHAR(20) AFTER name;MODIFY COLUMN是修改字段类型或默认值不会改字段名ALTER TABLE student MODIFY COLUMN phone VARCHAR(30) DEFAULT ;CHANGE COLUMN既改字段名又改类型两个参数都要写ALTER TABLE student CHANGE COLUMN phone mobile VARCHAR(30);DROP COLUMN是删除字段ALTER TABLE student DROP COLUMN mobile;我特别提醒两点第一生产环境对大表执行ALTER TABLE会锁表导致线上写入长时间不可用即使是5.7的在线DDL在某些场景下也会有性能影响所以大表变更前一定要评估数据量最好在低峰期操作第二CHANGE COLUMN的旧列名和新列名很容易写反格式是“CHANGE 旧字段名 新字段名 新类型”顺序搞错就会报“Unknown column”。这种错误我见过不止一次都是在熬夜上线的时候犯的。2.4 我见过最离谱的表结构设计聊到表结构忍不住分享几个反面案例。第一个是把日期存成字符串比如“2024-06-01”用VARCHAR(10)存储表面看起来没毛病但你想查“6月之后的数据”就得用字符串比较无法利用日期函数的索引优化数据量大了会非常痛苦。第二个是用VARCHAR存金额有一次导入数据时发现“9.99”变成“9.99000000001”就是因为应用层用字符串拼接导致精度丢失。第三个是用UUID当主键UUID是随机字符串插入时索引页会频繁分裂写入性能远不如自增BIGINT。建表的设计原则其实就一句话字段类型尽可能小存储内容尽可能标准。能选INT就不会选BIGINT能用DATE就不用VARCHAR能用DECIMAL就不用FLOAT。索引也不要贪多每个索引在写入时都要额外维护特别是在数据量大的情况下索引过多会让插入速度明显下降。一张表一般建议不超过5个索引必要的时候用组合索引覆盖多个查询条件。3. 记录增删改查的实用SQL写法3.1 SELECT查询别一上来就SELECT *查询是增删改查里最常用也最复杂的操作。先说最常见的槽点SELECT *。这句不是不能用而是要分场景。在数据量小、字段少的课程设计里完全没问题但如果表里有几十个字段、几百万条数据SELECT *会把所有字段捞出来浪费IO和内存。更关键的是如果你写了SELECT *即使后来表里加了几个大字段比如TEXT类型应用程序的返回结果集也会变大甚至会拖垮接口性能。正确做法是只查你需要的字段SELECT id, name, age FROM student WHERE age 18 ORDER BY age DESC LIMIT 10;WHERE条件里可以组合多个字段注意AND、OR的优先级AND比OR高所以多个条件时最好用括号明确逻辑。模糊查询用LIKE但要注意“%keyword%”这种写法无法利用索引数据量一大就全表扫描。关联查询JOIN要分清楚INNER JOIN和LEFT JOININNER JOIN只返回两边都匹配的记录LEFT JOIN会返回左表全部记录再补右表的匹配值没有匹配就填NULL。在统计报表里LEFT JOIN非常常见比如查“所有学生的选课情况”如果某个学生没选课INNER JOIN就会把他漏掉而LEFT JOIN能把他保留下来。聚合查询是另一个高频场景。配合GROUP BY使用时SELECT后面的非聚合字段一定要在GROUP BY里出现否则在MySQL 5.7默认关闭ONLY_FULL_GROUP_BY模式的情况下不会报错但查出来的值是不确定的。这其实是5.7埋的一个“雷”很多人没踩是因为数据量小看不出问题一旦数据多了就会得到莫名其妙的统计结果。我建议启用ONLY_FULL_GROUP_BY模式逼自己写出规范的SQL。加一下这段配置就能在会话里临时开启SET sql_mode ONLY_FULL_GROUP_BY;常用的聚合函数就是COUNT、SUM、AVG、MAX、MIN这五个配合GROUP BY做分组统计。COUNT(*)和COUNT(1)基本等价但如果字段值可能为NULL就别用COUNT(字段名)因为NULL不会被计数。3.2 INSERT插入单行、批量与查询插入INSERT是增删改查里的“增”写起来很简单但也有一些细节会影响效率和正确性。最基本的单行插入INSERT INTO student (name, age, gender) VALUES (张三, 20, 男);我特别推荐在INSERT语句里写明字段列表。如果你的表将来新增了字段不写字段列表的INSERT会因为“column count doesnt match value count”直接报错写了字段列表的旧SQL还能继续跑。批量插入一次插入多行用逗号分隔VALUES即可INSERT INTO student (name, age, gender) VALUES (李四, 21, 男), (王五, 22, 女), (赵六, 20, 男);批量插入比逐条INSERT快非常多因为减少了SQL解析和网络传输的开销实际开发中需要循环插入时也建议拼成批量SQL一次执行。INSERT ... SELECT可以把查询结果直接插入另一张表比如备份表、归档表这在做数据迁移时非常方便INSERT INTO student_bak (id, name, age) SELECT id, name, age FROM student WHERE age 22;MySQL 5.7还支持INSERT ... ON DUPLICATE KEY UPDATE它的作用是插入时遇到主键或唯一键冲突就执行更新操作。这在做数据同步时非常实用类似“有则更新无则插入”的UPSERT逻辑。不过要注意业务上对更新时机的判断别把已经修改过的数据又覆盖回旧值。3.3 UPDATE更新WHERE条件谁写谁负责UPDATE是增删改查里最容易出事的一类。最根本的原则是不加WHERE条件的UPDATE会更新表中所有记录。这不是危言耸听我亲眼见过有人想改一条用户数据执行到一半才发现WHERE条件丢了生产库几十万条记录的某个字段全被改了一遍。如果你的MySQL已经开启了binlog还能靠日志恢复但恢复过程极其痛苦。一条规范的UPDATEUPDATE student SET age 23, update_time NOW() WHERE id 5;在MySQL 5.7里UPDATE语句的SET子句可以包含多个字段修改多个字段时注意用逗号分隔不能写“SET age 23 AND update_time NOW()”这个AND是逻辑运算符不是逗号写错不会直接报错会把AND表达式的结果当成age的新值在MySQL中把布尔值强转成数字后就是0或1容易造成数据异常。关联更新也很常用比如根据另一张表的结果来更新当前表。MySQL的语法是UPDATE JOINUPDATE student s LEFT JOIN sc ON s.id sc.student_id SET s.total_score s.total_score 10 WHERE sc.course_id 1;这种多表UPDATE看着复杂其实就是先JOIN出符合条件的记录集合再对集合进行更新。注意WHERE条件要写清楚别把关联关系弄错。3.4 DELETE删除DELETE、TRUNCATE、DROP三兄弟的区别删除操作的风险等级是最高的因为误删之后基本只能靠备份恢复。DELETE、TRUNCATE、DROP这三个语句都能让数据消失但效果完全不同。这张表解释了三者的核心差异对比项DELETETRUNCATEDROP删除对象记录可带WHERE表内所有记录整个表是否保留表结构保留保留不保留能否带WHERE可以不可以不可以是否触发事务回滚可以回滚部分场景不可回滚不可回滚执行速度慢逐行删除快直接释放数据页最快按这个就很好记了想删部分记录用DELETE加上WHERE想快速清空整张表但保留表结构用TRUNCATE想把表彻底扔掉用DROP。执行TRUNCATE和DROP之前一定要确认因为它们在InnoDB下虽然TRUNCATE在事务里可能能够回滚但因为DDL语句隐式提交一旦执行完数据基本找不回来了。MySQL 5.7还有一个安全机制叫SQL_SAFE_UPDATES开启后在DELETE或UPDATE不带WHERE或者WHERE不包含索引键时会拒绝执行SET SQL_SAFE_UPDATES 1;我在做危险操作前一定会先开这条相当于给自己加了一道保险。完成操作后记得改回去。3.5 事务和锁多记录操作的安全网聊增删改查不能不提事务。事务最经典的例子就是转账A账户扣100元B账户加100元这两条UPDATE必须同时成功或同时失败否则就会出现资金不平的严重事故。在MySQL中事务用START TRANSACTION开始用COMMIT提交用ROLLBACK回滚START TRANSACTION; UPDATE account SET balance balance - 100 WHERE user_id 1; UPDATE account SET balance balance 100 WHERE user_id 2; COMMIT;如果第二条UPDATE执行失败整个事务都可以回滚INNODB的事务日志会帮我们把第一条操作也撤销。五个字概括就是“要么全成功要么全失败”这就是事务ACID中的原子性。InnoDB还通过行级锁保证并发时的隔离性同一行数据被事务A修改后未提交时事务B修改同一行会进入等待状态直到事务A提交或回滚。很多新手在课程设计里不写事务单个INSERT、UPDATE、DELETE也没事但一旦涉及跨表操作或业务链路变长没有事务包裹就会留下严重隐患。我建议所有多步骤写操作都尽量放进事务里并在应用层做好异常捕获这样出的问题才能有效回滚代码健壮性也会提升很多。4. 完整实操学生选课系统从建库到跑通4.1 准备数据库与数据模型说了这么多理论和坑我用一个学生选课系统的项目来完整演示一遍增删改查。这也是数据库课程设计里最高频的一个项目。先明确数据模型这里有三张核心表student学生表、course课程表、sc选课表。它们之间的关系是一个学生可以选多门课一门课可以被多个学生选所以学生和课程是多对多关系中间的sc表就是关联表。我先把连接方式说清楚。命令行下执行mysql -uroot -p输入密码后进入MySQL命令行。如果你是用的Navicat、DataGrip或Workbench图形界面操作也同理。建议自己动手敲SQL课程设计答辩时老师会让你现场操作你要是连命令行都不熟就很尴尬。4.2 建库建表语句逐段拆解创建数据库student_system字符集用utf8mb4CREATE DATABASE IF NOT EXISTS student_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_system;接着建student表包含学生学号、姓名、性别、出生日期和入学时间等字段。这里学号用BIGINT当主键没有用自增因为学号是业务唯一的CREATE TABLE student ( id BIGINT UNSIGNED PRIMARY KEY COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender ENUM(男, 女) NOT NULL DEFAULT 男 COMMENT 性别, birth_date DATE COMMENT 出生日期, enroll_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 入学时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表;ENUM是MySQL里比较特殊的类型直接把可枚举值用字符串列出来内部存的是数字索引既省空间又直观。不过ENUM的缺点是后续想加新值时修改表定义会比较麻烦所以实际项目中有人喜欢用TINYINT加注释来表示状态。这里用它纯粹是因为演示起来直观。接下来建course课程表CREATE TABLE course ( id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT 课程ID, name VARCHAR(100) NOT NULL UNIQUE COMMENT 课程名称, credit DECIMAL(3,1) NOT NULL DEFAULT 2.0 COMMENT 学分, teacher VARCHAR(50) COMMENT 授课教师 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表;最后建sc选课表两个外键分别关联student和courseCREATE TABLE sc ( id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, student_id BIGINT UNSIGNED NOT NULL, course_id INT UNSIGNED NOT NULL, score DECIMAL(5,2) COMMENT 成绩, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course (student_id, course_id), FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (course_id) REFERENCES course(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT选课表;这里有两个细节值得重视。第一sc表加了联合唯一键uk_student_course保证同一个学生不能重复选同一门课这是靠数据库约束来防止应用层重复提交。第二两个外键会强制要求插入sc记录前student和course里必须有对应记录这对数据完整性是好处但如果你一上来就插sc表数据会先插入student和course。4.3 插入与查询跑一遍基础数据先往student表插入学生数据INSERT INTO student (id, name, gender, birth_date) VALUES (2024001, 张三, 男, 2004-05-12), (2024002, 李四, 女, 2005-01-20), (2024003, 王五, 男, 2003-11-03);再插入课程数据INSERT INTO course (name, credit, teacher) VALUES (数据库原理, 3.0, 陈老师), (计算机网络, 2.5, 刘老师), (数据结构, 3.5, 赵老师);最后插入选课数据INSERT INTO sc (student_id, course_id) VALUES (2024001, 1), (2024001, 2), (2024002, 1), (2024003, 3);到现在为止我们就完成了INSERT操作。接下来是查询我从简单到复杂逐个演示。先查所有学生的基本信息SELECT id AS 学号, name AS 姓名, gender AS 性别 FROM student;如果想统计每门课的选课人数用GROUP BY加COUNTSELECT c.name, COUNT(sc.student_id) AS total_students FROM course c LEFT JOIN sc ON c.id sc.course_id GROUP BY c.id, c.name;注意这里用的是LEFT JOIN而不是INNER JOIN因为我们要把所有课程都显示出来没学生选的课总数就是0。如果改用INNER JOIN没被选的课会被过滤掉这在统计报表里就是错误的输出。还想查出张同学选了哪些课程用INNER JOIN把三张表连起来SELECT s.name, c.name, sc.score FROM student s JOIN sc ON s.id sc.student_id JOIN course c ON sc.course_id c.id WHERE s.id 2024001;这三个JOIN的思路其实就是从student出发先通过sc找到选课关联再通过course找到课程信息。4.4 更新与删除模拟一次选课调整接下来模拟一次业务操作。张同学退掉“计算机网络”这门课如果选课表里有分数记录那么这条记录就应该被删除DELETE FROM sc WHERE student_id 2024001 AND course_id 2;只执行这一条student和course都毫发无损这就是“删除记录”与“删除表”的最大区别。如果业务上要求的是删除整张选课表里所有历史数据那么用TRUNCATETRUNCATE TABLE sc;上面这句在课程设计文档里可以作为“系统支持数据清理功能”的说明但是实际使用时一定想清楚。下面模拟一次选课变更王五同学原来是“数据结构”被退回改成选了“计算机网络”-- 先删除旧选课记录 DELETE FROM sc WHERE student_id 2024003 AND course_id 3; -- 再插入新记录 INSERT INTO sc (student_id, course_id) VALUES (2024003, 2); -- 查一次确认结果 SELECT s.name, c.name FROM sc JOIN student s ON sc.student_id s.id JOIN course c ON sc.course_id c.id WHERE s.id 2024003;这三条操作最好放在一个事务里执行不然可能出现删了旧选课但新课程插入失败的情况学生就“空档”了。改成事务写法更安全START TRANSACTION; DELETE FROM sc WHERE student_id 2024003 AND course_id 3; INSERT INTO sc (student_id, course_id) VALUES (2024003, 2); COMMIT;最后模拟一次非常重要的数据维护给所有学生都加1分的操作在真正的考试调整时也会用到UPDATE sc SET score score 1 WHERE course_id 1;这条语句没有牵扯到所有记录已经通过WHERE限制了课程属于相对安全的操作。但必须再强调一句如果是全校统一加分没有WHERE的UPDATE会更新整张表后果就是你把所有考生的成绩都改了。操作前先SELECT COUNT(*)数一遍受影响行数确认无误再更新这是我一直强调的习惯。5. 常见问题与排查技巧实录5.1 中文乱码问题乱码是新手阶段最高频的问题往往不是某个单一原因而是字符集在多个环节没有对齐。MySQL里字符集从服务器层一直传到字段层只要某一层的编码和客户端编码不一致就会乱码。最典型的现象是在命令行里插入中文直接显示为“???”或者“ERROR 1366 Incorrect string value”。排查步骤按这个顺序来先看数据库、表、字段的字符集是不是utf8mb4如果建表时写的是latin1那一定是乱码根源SHOW CREATE TABLE student\G再看连接层字符集在MySQL命令行里执行SET NAMES utf8mb4;这个命令会把客户端、连接、返回结果的字符集一次性设置成utf8mb4。注意乱码问题不只是数据库端的设置显示端的终端工具也必须是UTF-8。Windows里的命令行如果没把代码页切到65001哪怕数据库全是对的显示依然会乱。用图形化工具连接时在连接参数里也要把编码改成UTF-8Navicat默认抓的是“自动”。5.2 表名或字段名总报错保留字冲突我第一次写一个关于“订单”的表时直接写了CREATE TABLE order (...),结果报“You have an error in your SQL syntax”。原因是order是MySQL的保留字用在排序语法里。类似的高频保留字还有group、desc、key、select、condition等。解决办法有两个一是名字尽量避免用保留字二是实在要用就加反引号CREATE TABLE order ( id INT PRIMARY KEY, ... );这里要强调一下反引号是MySQL独有的引用格式Oracle和PostgreSQL用的是双引号。如果以后要跨数据库迁移尽量避免保留字作为表名字段名不然每一条SQL都要加反引号维护成本很高。5.3 更新删除卡死锁与事务问题遇到过好几次“UPDATE执行很久都没反应”的情况十有八九是行锁被别的事务占用了。InnoDB默认行级锁但前提是更新条件走了索引如果WHERE条件没走索引行级锁会升级成表级锁把整张表锁住严重影响并发。排查锁问题可以用以下两个命令SHOW PROCESSLIST;SELECT * FROM information_schema.INNODB_TRX\GSHOW PROCESSLIST能看到哪些连接在等待锁INNODB_TRX能看到当前活跃事务。如果发现某个事务一直没提交它持有的锁就会一直不释放其他会话的操作只能一直等。处理方式一般就是把那个事务KILL掉或者把还没提交的事务手动ROLLBACK。实践中我们会把所有要执行的事务代码都设置在极短时间内完成并提交避免因为程序端代码发生异常导致事务一直不关闭把表锁死。如果你的应用是用连接池还要注意连接池需要设置合理的超时时间否则应用出故障后连接一直挂着也会导致锁的问题放大。5.4 外键约束报错的排查插入sc表数据时如果报外键约束错误“Cannot add or update a child row”说明student表或course表里没有对应的主键记录。排查思路比较简单SELECT * FROM student WHERE id 你的学号; SELECT * FROM course WHERE id 你的课程ID;如果查不到就说明外键指向的记录不存在先插入父表数据再插入子表。还有一种情况是外键对应的数据类型不一致比如student.id是BIGINT UNSIGNEDsc表里student_id写成了INT UNSIGNED字段类型不完全匹配就会报错“Cannot add foreign key constraint”。建表时字段类型务必要一致连UNSIGNED和长度这些都要对齐。5.5 备份与误删的兜底策略关于误删最实用的底线就是备份。MySQL 5.7提供了mysqldump工具命令格式如下mysqldump -uroot -p student_system student_system_backup.sql如果要恢复mysql -uroot -p student_system student_system_backup.sql如果是课程设计阶段数据量不大每次大改动前导出一次也花不了几秒钟。我在实际项目中还会专门把mysqldump加入到crontab里每天凌晨定时备份一次这样即使哪天误操作了最多丢一天的数据。有一个细节值得说mysqldump默认备份的是整个库恢复时也会执行建库和USE语句。如果你只想备份单张表在后面加上表名即可mysqldump -uroot -p student_system sc sc_backup.sql备份文件内容本身也是文本形式可以直接打开查看里面生成的CREATE TABLE语句和INSERT语句这也是学习别人表结构设计的一个非常直接的途径。写在最后的一些经验这套增删改查的内容说到底是数据库操作里最基础但也最重要的一块。AI可以帮你生成复杂SQL但它不知道你的表结构里哪个字段是TEXT、哪张表数据量过千万、哪些字段被多人同时更新。这些判断只能靠你自己理解增删改查的原理和细节才能在关键时刻不慌。我自己就养成了两个习惯一个是所有写操作前先写SELECT验证目标和影响行数一个是高危操作前先备份再进行。这两步看着笨但真的能让你少熬好几个通宵。最后再分享一个小技巧在上生产环境之前把建表语句和CRUD命令在本地库里跑一遍把常见的报错都看一遍实际踩一遍坑比看十篇教程都管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价