news 2026/10/2 3:25:00

MySQL入门实战:从建表到增删查改的完整CRUD操作指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL入门实战:从建表到增删查改的完整CRUD操作指南

MySQL入门绕不开的坎,就是增删查改这四个动作,也就是常说的CRUD。很多教程把增删查拆得七零八落,讲插入的只讲插入,讲查询的只讲查询,读者看完感觉自己什么都见过,可真到工作里要动手建表、写查询、改数据的时候,还是不知道从哪里下手。这篇内容就是为了解决这个问题:我拿一张学生信息表做贯穿案例,把MySQL基础操作中的增删查改从语法到实战一路走通,每一步都告诉你为什么这么写、不这么做会踩什么坑。适合刚接触数据库的同学,也适合想把自己零碎的MySQL知识系统过一遍的人。

1. 动手前的准备工作

1.1 环境与连接工具

先别急着敲SQL,一上来就操作很容易被各种小问题劝退。MySQL本身要先装好,装完以后你得有个趁手的连接工具。

命令行是默认选项,也是最稳妥的。Windows下打开cmd,Linux/Mac下打开终端,输入类似这样的命令就能连上本地MySQL:

mysql -uroot -p

输入密码后,看到mysql>提示符就说明进来了。命令行适合练基本功,所有SQL都能跑,但缺点也很明显,查询结果多了以后排版不够直观。

图形化工具我建议新手至少准备一个。最常用的是Navicat和DBeaver,另外MySQL官方也提供免费的MySQL Workbench。这三者选哪个都行,只要能把数据库连上、能看表结构、能执行SQL就可以。DBeaver是免费的跨平台工具,社区版就够用;Navicat功能强大,但很多功能需要付费授权,而且网上流传的所谓“破解版”既不合规,也可能带有安全风险,我自己从来不用,也建议你使用正版或免费工具。

连接工具本质上只是个壳,它把你要执行的SQL发给MySQL服务器,再把结果渲染出来。所以不管用哪个工具,核心还是SQL本身。

1.2 建库建表:数据类型先选对

操作数据的顺序一般是:先建数据库,再建表,最后才能往里塞数据。创建一个数据库很简单:

CREATE DATABASE school DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

这里一定要指定utf8mb4字符集。MySQL老版本默认的utf8其实是utf8mb3,它存不了emoji和部分生僻字,报错的时候往往只给你一个冷冰冰的“Incorrect string value”,排查半天才发现是字符集问题。utf8mb4是utf8的超集,能兼容更多字符,现在新建库表直接用utf8mb4就行了。

建表是增删查改之前最关键的一步,表结构定得不好,后面CRUD全都别扭。拿学生表来说,基础字段设计如下:

CREATE TABLE student ( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', name VARCHAR(50) NOT NULL COMMENT '姓名', gender ENUM('male', 'female') DEFAULT NULL COMMENT '性别', age TINYINT UNSIGNED DEFAULT 0 COMMENT '年龄', class_name VARCHAR(50) DEFAULT NULL COMMENT '班级', score DECIMAL(5,2) DEFAULT 0.00 COMMENT '总分', created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表';

几个字段类型的选择思路要讲清楚。id用INT UNSIGNED,主键一般不用负数,无符号整数可以让正数范围翻倍;age用TINYINT UNSIGNED,因为人的年龄撑死一百多,TINYINT范围是0~255,完全够用,还省空间;score用DECIMAL(5,2),因为浮点数的精度在MySQL里不靠谱,存金额、分数这类需要精确计算的数值,一定要用定点数DECIMAL;created_at用DATETIME,配合默认值CURRENT_TIMESTAMP,插入数据的时候会自动记录当前时间。

ENGINE=InnoDB也必须写上。InnoDB是MySQL默认引擎,支持事务、行级锁、外键,生产环境基本都用它。MyISAM虽然查询快一点,但不支持事务,崩溃后恢复能力也差,除非你有特殊理由,否则一律InnoDB。

2. INSERT:把数据装进口袋

2.1 单条插入与批量插入

INSERT做的事就是给表里加数据。最简单的单条插入写法:

INSERT INTO student (name, gender, age, class_name, score) VALUES ('张三', 'male', 18, '高一(3)班', 580.5);

表名后面括号里写字段名,VALUES后面写对应值。这里有个习惯问题:我见不少人写INSERT时省略字段列表,直接写INSERT INTO student VALUES (...),这种写法要求值的顺序必须和表结构完全一致,一旦表加了字段或者调整了顺序,这条SQL就废了。生产中表结构隔三差五会变,所以强烈建议每次都用显式字段列表。

一次插入多条数据时有更高效的做法,把多个值组用逗号连起来:

INSERT INTO student (name, gender, age, class_name, score) VALUES ('李四', 'female', 17, '高一(2)班', 592.0), ('王五', 'male', 18, '高一(3)班', 561.0), ('赵六', 'female', 17, '高一(1)班', 598.5);

批量插入不仅是写法更紧凑,更是性能上的选择。每条INSERT单独执行,MySQL每次都要重新解析SQL、做权限检查、记录日志,批量插入可以把这些开销摊薄到每条记录上。几千条数据场景下,这个差别就已经很明显了。我实测往一张10万行级别的表里灌数据,单条逐行插入可能要几分钟,改成一次插入500条,速度能提升一个数量级。

2.2 插入时的常见坑

第一,字段和值的数量必须严格对应。少写一个值会报Column count doesn't match value count。这属于低级错误,但越忙的时候越容易犯。

第二,要注意默认值的行为。上面表里的gender、class_name是允许为NULL的,created_at有默认值。所以可以只插入部分字段:

INSERT INTO student (name, age) VALUES ('周七', 18);

这样gender、class_name自动是NULL,score会用默认值0.00,created_at自动填充当前时间。反过来,如果你连id都想自己指定,也不是不行,id字段因为是AUTO_INCREMENT,你手动给个值也可以,但除非有特殊场景,我建议永远不要手动指定自增主键,让MySQL自己分配就好。

第三,字符串里的单引号需要转义。比如学生叫“O'Brien”,直接写会语法错误,要写成:

INSERT INTO student (name) VALUES ('O\'Brien');

另外,如果需要插入的数据在唯一键上冲突时想要“有则更新、无则插入”,可以用ON DUPLICATE KEY UPDATE:

INSERT INTO student (id, name, age) VALUES (1, '张三', 19) ON DUPLICATE KEY UPDATE name = VALUES(name), age = VALUES(age);

这个方法在批量同步数据时非常好用,但注意它依赖唯一索引或主键,没有索引限制的时候MySQL不知道“重复”指的是什么。

3. SELECT:把数据翻出来看

3.1 基础查询和条件过滤

查询是CRUD里最常写、也最值得花时间学的一部分。最简单的全表查询:

SELECT * FROM student;

*表示所有字段,开发调试的时候很方便,但生产环境尽量少用SELECT *,尤其是表字段多、数据量大的时候。原因有两个:一是多查了很多用不到的字段,白白增加网络传输和内存消耗;二是如果哪天表结构加了字段,代码里拿结果集的顺序可能就乱了。所以查什么字段就写什么字段:

SELECT id, name, age FROM student;

条件过滤是查询的灵魂。WHERE子句用来指定过滤条件,比如查所有18岁的学生:

SELECT name, age FROM student WHERE age = 18;

条件里可以用各种运算符,=、>、<、>=、<=、!=,以及逻辑运算符AND、OR、NOT。比如查高一(3)班且分数大于560的:

SELECT name, class_name, score FROM student WHERE class_name = '高一(3)班' AND score > 560;

模糊查询用LIKE。比如查所有姓“张”的学生:

SELECT name FROM student WHERE name LIKE '张%';

%是通配符,代表任意长度的任意字符;_代表单个字符。这里有个性能问题:LIKE '%张'这样以通配符开头的写法,会导致索引失效,全表扫描。数据量小无所谓,数据量大的时候一定要避免。

范围查询可以这样写:

-- 用 AND 写法 SELECT name, age FROM student WHERE age >= 16 AND age <= 18; -- 用 BETWEEN 写法,效果一样 SELECT name, age FROM student WHERE age BETWEEN 16 AND 18;

3.2 排序、去重与分页

查询结果默认是按物理存储顺序返回的,大多数时候不是你想要的顺序。用ORDER BY控制排序:

-- 按分数从高到低 SELECT name, score FROM student ORDER BY score DESC; -- 按年龄从小到大,年龄相同时按分数从高到低 SELECT name, age, score FROM student ORDER BY age ASC, score DESC;

DESC表示降序,ASC表示升序,默认是ASC。多字段排序时,按写的顺序逐级比较,这也是考试里常考的知识点。

去重用DISTINCT。比如想看看学生都分布在哪几个班级:

SELECT DISTINCT class_name FROM student;

注意,SELECT DISTINCT name, class_name去重的是(name, class_name)的组合,不是只对第一个字段去重,这个容易理解错。

分页是生产环境非常高频的需求,用LIMIT:

-- 跳过前0条,取10条,即第1页 SELECT name, score FROM student ORDER BY score DESC LIMIT 0, 10; -- 跳过前10条,取10条,即第2页 SELECT name, score FROM student ORDER BY score DESC LIMIT 10, 10;

LIMIT offset, count的写法里,offset是从第几条开始跳,count是取多少条。记住分页公式:第N页的数据,offset = (N-1) * pageSize。这里注意,深分页是个性能杀手,LIMIT 1000000, 10需要MySQL先数出前面100万条再丢弃,非常慢。解决思路通常是先用索引定位到起始位置再往后取,或者记录上一页最后一条数据的id作为下一页起点。

3.3 聚合统计与GROUP BY

光把数据列出来还不够,业务上经常要“统计”。聚合函数就是干这个的,常见的有COUNT、SUM、AVG、MAX、MIN。比如:

-- 学生总数 SELECT COUNT(*) FROM student; -- 分数平均值 SELECT AVG(score) FROM student; -- 最高分和最低分 SELECT MAX(score), MIN(score) FROM student;

把统计按组拆开,就得用GROUP BY。比如统计每个班有多少人、平均分多少:

SELECT class_name, COUNT(*) AS cnt, AVG(score) AS avg_score FROM student GROUP BY class_name;

这里AS是给查询结果字段起别名,显示的时候更好看懂。GROUP BY之后如果想过滤组,不能再用WHERE,得用HAVING。比如只要平均分大于580的班级:

SELECT class_name, AVG(score) AS avg_score FROM student GROUP BY class_name HAVING avg_score > 580;

WHERE和HAVING的区别很多新手搞混:WHERE是在分组之前对每一行做过滤,HAVING是在分组之后对每一组做过滤。这个顺序不能反,否则语义就错了。

3.4 多表查询的入门思路

基础操作阶段可以只操作单表,但实际开发中几乎没有只查一张表的业务。拿学生表来说,如果每个学生还有一张成绩明细表,查询“学生姓名+他的各科成绩”,就需要把两张表连起来。JOIN就是干这个的:

SELECT s.name, g.subject, g.score FROM student s INNER JOIN grade g ON s.id = g.student_id;

JOIN的核心逻辑是:把左表的每一行拿出来,跟右表的每一行做匹配,匹配条件是ON后面写的等式。INNER JOIN只保留两边都匹配得上的行,LEFT JOIN保留左表所有行,匹配不上则右表字段补NULL。多表查询的细节能写好几篇文章,这里先明白“JOIN是把两张表按关联条件拼成一张大表”这个思路就够了。

4. UPDATE:修改已有数据

4.1 基本更新语法

UPDATE用来修改表中的数据。基本语法:

UPDATE student SET age = 19 WHERE name = '张三';

从逻辑上讲,UPDATE做的事是:找到符合WHERE条件的行,把这些行的age字段改成19。这里最重要的就是WHERE条件。如果不写WHERE,MySQL会把整张表的所有行都更新:

-- 危险!把所有人的年龄都改成19 UPDATE student SET age = 19;

很多初学者都在这里栽过跟头。MySQL的默认安全模式下,不带WHERE或WHERE里没有使用索引的UPDATE会直接报错:You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column。这其实是保护你的机制,它能拦下“忘记写WHERE”的低级失误。所以我建议新手把安全模式开着,别急着关掉它。

4.2 更新多个字段和批量更新技巧

更新多个字段用逗号分隔:

UPDATE student SET age = 19, score = 590.5 WHERE id = 1;

批量更新不同记录的不同值,有一种常见做法是用CASE WHEN。比如要把“高一(1)班”的分数统一加5分,“高一(2)班”的分数统一加10分:

UPDATE student SET score = CASE class_name WHEN '高一(1)班' THEN score + 5 WHEN '高一(2)班' THEN score + 10 ELSE score END;

这种写法比写多条UPDATE语句更高效,但业务逻辑复杂的时候可读性会下降,需要配合注释使用。

更新的时候还有一个关键点:如果你更新多行,但中途某一行失败,前面的修改会怎样?这取决于事务。默认情况下,如果SQL语句本身出错,这一条语句执行的修改会全部回滚,不会只成功一半;如果你把它们放在一个显式事务里,就可以控制多条语句一起成功或一起失败。实际上,在命令行里逐条执行不带提交的UPDATE,MySQL默认是自动提交的,也就是说每执行一条,立刻生效,没有后悔药。所以更新数据之前,建议你先跑一遍相同的SELECT,确认要改的就是这些行:

-- 先确认 SELECT id, name, age FROM student WHERE class_name = '高一(1)班'; -- 再更新 UPDATE student SET age = age + 1 WHERE class_name = '高一(1)班';

这个习惯看上去平平无奇,但能救你无数次。

5. DELETE:把数据抹掉

5.1 DELETE语法与安全性

DELETE用来删除表中的数据:

DELETE FROM student WHERE id = 1;

跟UPDATE一样,DELETE最关键的也是WHERE。不写WHERE就是清空整张表:

-- 危险!删除所有学生 DELETE FROM student;

关于DELETE,我见过的最典型事故是:想删一个学生,条件写错了,把整个班级的都删了;或者干脆忘了写WHERE,全表没了。预防办法就是前面说的“先SELECT后DELETE”,先查出来看看要删的是不是这些。

删除操作还有外键约束的问题。如果其他表里面有关联到student表的数据,DELETE的时候可能会报Cannot delete or update a parent row: a foreign key constraint fails。这是InnoDB在保护数据完整性:不能把别的表还在引用的父记录删掉。解决办法是先把子表里的关联数据处理掉,或者在外键上配置ON DELETE CASCADE,但这属于进阶内容,新手阶段先把概念搞清楚。

5.2 DELETE、TRUNCATE和DROP怎么选

很多新人会把“删除数据”和“删除表”混在一起。实际上有三个操作长得像但作用完全不同:

操作作用能否带WHERE事务支持自增ID速度
DELETE删除表数据能支持,可回滚不会重置,继续递增慢(逐行删除)
TRUNCATE清空表不能大多数情况下隐式提交,不可回滚重置为初始值快(直接重建表)
DROP删除整张表不能不可回滚(无备份的前提下)—最快

一句话总结:想删部分数据,用DELETE;想清空表且希望自增ID重新从1开始,用TRUNCATE;想彻底删掉表结构和数据,用DROP。后续还能不能用这张表,取决于你是哪一种。

TRUNCATE的“不可回滚”需要特别强调一下。虽然MySQL 8.0的文档显示TRUNCATE在某些情况下可以回滚,但生产环境千万别赌这个行为。我身边真实案例:有同事用TRUNCATE清了一张日志表,后来发现数据还要用,因为没有备份,悔得肠子都青了。所以TRUNCATE和DROP这种高风险操作,执行前最好先备份。

6. 实战中常见的坑与排查

6.1 高频报错速查表

我在带新人和日常工作中,收集了这些MySQL新手高频报错,整理成表格,遇到问题可以对照着排查:

报错信息片段含义解决思路
Unknown column 'xxx' in 'field list'字段名不存在检查拼写,SHOW COLUMNS FROM 表名
Table 'xxx' doesn't exist表不存在检查是否选对数据库,USE xxx
Duplicate entry '1' for key 'PRIMARY'主键重复主键自动生成,不要手动插入相同的值
Incorrect string value字符集问题表和字段都用utf8mb4
Data too long for column值超长检查字段长度定义是否合理
Column count doesn't match value count插入的字段数和值数不一致检查INSERT语句字段列表
Cannot delete or update a parent row外键约束阻止先处理子表数据
You are using safe update mode缺少WHERE的安全保护判断是否真的需要全表更新,或补充WHERE

6.2 一定要养成的几个实操习惯

第一,生产环境操作前先备份。真正常犯的错不是SQL写错,而是没给自己留退路。哪怕只是更新一张小表,有备份就敢放手做,没备份就只能靠祈祈祷。

第二,养成看执行计划的习惯。可以简单一点:遇到查询变慢了,在SQL前面加个EXPLAIN。它会告诉你MySQL是怎么执行这条语句的,有没有用到索引,扫描了多少行。这是从“会写SQL”到“会写好的SQL”的分水岭。

EXPLAIN SELECT name, score FROM student WHERE age = 18;

不用怕看不懂输出,先看type和rows两列就够起步了。type是ALL说明全表扫描,值得警惕;rows是预估扫描行数,越小越好。

第三,事务处理要心里有数。InnoDB支持事务,多步数据变更建议手动控制:

START TRANSACTION; UPDATE account SET balance = balance - 100 WHERE id = 1; UPDATE account SET balance = balance + 100 WHERE id = 2; COMMIT;

先跑一遍,确认没问题再COMMIT,有问题就ROLLBACK。这比一条条自动提交要安全得多。

第四,字段注释和SQL注释别偷懒。建表的COMMENT、SQL脚本里的注释,看起来是小事,但接手的人或者几个月后的你自己,会非常感谢这些注释。

我在实际使用中发现,很多数据库问题其实不是SQL语法不会写,而是对“这条语句到底会对哪些数据生效”缺乏感知。增删查改四个操作,最危险的不是写不出来,而是写出来之后一执行,影响的范围和自己想的不一样。所以每次写UPDATE和DELETE,我都会下意识地把前面的SELECT再执行一遍,确认范围之后再动手。这套习惯帮我避过了很多次潜在事故。

MySQL基础操作往后还能延伸很多方向,比如索引优化、事务隔离级别、存储过程、主从复制,但所有进阶内容都建立在今天这套CRUD的基础上。把增删查改练熟、把常见坑记住,后面学什么都会顺畅很多。建议你把这篇文章里的SQL语句照着敲一遍,敲完你就发现,数据库这事儿没有想象中那么神秘。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 3:24:59

MySQL删除操作详解:drop、delete、truncate的区别与实战避坑

干过几年数据库的人&#xff0c;基本都被问过一个问题&#xff1a;drop、delete、truncate到底有什么区别&#xff1f;前两天还有个朋友找我&#xff0c;说他在测试环境执行了一条不该执行的delete&#xff0c;结果整个表的数据全没了&#xff0c;幸好有备份&#xff0c;不然直…

作者头像 李华
网站建设 2026/10/2 3:24:57

K8S到底解决了什么问题?从容器编排到生产落地的核心原理

听到“K8S是用来解决什么问题的&#xff1f;”这种问题&#xff0c;第一反应通常是先立正&#xff0c;因为这问题看着基础&#xff0c;但真能一句话讲清楚的人并不多。网上铺天盖地都是安装部署教程、面试题、operator案例&#xff0c;反而把最核心的“它到底为什么存在”给说糊…

作者头像 李华
网站建设 2026/10/2 3:24:24

Java重载、重写与多态:从编译期到运行期的彻底解析

“重载&#xff08;Overload&#xff09;、重写&#xff08;Override&#xff09;、多态&#xff08;Polymorphism&#xff09;”这三个词&#xff0c;几乎每个学面向对象编程的人都绕不过去。但我发现一个很有趣的现象&#xff1a;网上搜这三个词&#xff0c;出来的资料有一半…

作者头像 李华
网站建设 2026/10/2 3:24:24

YOLO夜间目标检测实战:数据集检查、训练配置与避坑指南

简介&#xff1a;面向夜间车辆与行人检测的YOLO格式数据集&#xff0c;覆盖行人、自行车、汽车、狗四类目标&#xff0c;适用于YOLOv5及后续版本的训练、微调与算法改进。数据按YOLOv5目录结构存放&#xff0c;标签采用中心点坐标加宽高的归一化格式&#xff0c;训练集8410张图…

作者头像 李华
网站建设 2026/10/2 3:24:24

基于Hadoop+Spark的健康风险预测系统:大数据毕设完整技术路线

选题这事儿&#xff0c;每年都有一批又一批的计算机专业毕业生卡在第一步。有人纠结技术栈太旧没亮点&#xff0c;有人担心难度太高做不完&#xff0c;还有人做完之后发现论文根本没什么可写的。今天聊这个“基于HadoopSpark的健康风险预测系统”&#xff0c;算是大数据方向里一…

作者头像 李华
网站建设 2026/10/2 3:23:36

纯Python手写SSH MCP Server:让AI安全连接服务器

作为一个常年折腾自动化工具的人&#xff0c;我一直在思考一个问题&#xff1a;AI 再聪明&#xff0c;如果只能停留在对话框里输出文字&#xff0c;那它的价值就折损了大半。真正让 AI 从“顾问”变成“执行者”的&#xff0c;是让它能直接操作真实环境——比如连上你的服务器&…

作者头像 李华