1. 项目概述:从零上手数据库操作
刚接触后端开发或者数据分析,你绕不开的一个坎就是数据库。而说到数据库,MySQL绝对是那个你最先遇到、也最常打交道的“老朋友”。很多人一上来就被“增删改查”这四个字吓到,觉得这是多么高深的技术。其实不然,这恰恰是数据库世界最基础、最核心的四个动作,就像学开车要先学会前进、后退、左转、右转一样。掌握了它们,你才算真正拿到了操作数据的“驾照”。
所谓“增删改查”,对应的就是SQL语言中的四条核心指令:INSERT(增加数据)、DELETE(删除数据)、UPDATE(修改数据)和SELECT(查询数据)。无论你未来是做网站用户管理、电商订单处理,还是做数据分析报表,本质上都是在和这四条指令打交道。我见过不少新手,一上来就琢磨复杂的联表查询和事务处理,结果连一条完整的数据都插不进去,基础不牢,地动山摇。这篇文章,我就以一个老司机的视角,带你手把手、掰开揉碎地过一遍MySQL表的增删改查。我们不只讲语法,更重点讲每个操作背后的意图、常见的坑点以及我积累下来的一些实操心得,目标是让你看完就能在自己的电脑上动手练起来,真正理解数据是怎么“活”起来的。
2. 环境准备与数据表搭建
在开始“驾驶”数据之前,我们得先有“车”和“路”,也就是MySQL环境和一张用于练习的数据表。我强烈建议你不要只用脑子记,一定要跟着步骤实际操作一遍,手感是看多少遍都换不来的。
2.1 MySQL安装与快速启动
对于初学者,在Windows上安装MySQL最省心的方式是使用官方安装包(MySQL Installer)。它会帮你搞定服务安装、路径配置等一堆麻烦事。安装过程中,记得设置好root用户的密码,这个密码务必牢记。安装完成后,你可以通过Windows服务管理器启动MySQL服务,也可以使用命令行net start mysql来启动。
我更推荐使用命令行工具mysql -u root -p来连接数据库,输入密码后,看到mysql>提示符,就说明你已经成功进入了MySQL的交互世界。图形化工具(如MySQL Workbench、Navicat)固然直观,但初期多用命令行,能帮你更扎实地理解SQL语句的执行过程,避免成为只会点按钮的“界面工程师”。
2.2 创建练习用的数据表
我们创建一个students(学生信息)表来贯穿整个练习,它包含一些常见类型的字段:
CREATE DATABASE IF NOT EXISTS practice_db; USE practice_db; CREATE TABLE students ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID,主键,自增长', name VARCHAR(50) NOT NULL COMMENT '学生姓名,非空', age TINYINT UNSIGNED COMMENT '年龄,无符号小整数', gender ENUM('男', '女') DEFAULT '男' COMMENT '性别,枚举类型', enrollment_date DATE NOT NULL COMMENT '入学日期', score DECIMAL(5, 2) COMMENT '成绩,小数点后两位' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '学生信息表';逐行解析与注意事项:
CREATE DATABASE / USE:先创建(如果不存在)并切换到我们的练习数据库。这是你的“工作间”。id INT PRIMARY KEY AUTO_INCREMENT:这是表的“身份证号”。PRIMARY KEY(主键)确保每一行数据唯一,AUTO_INCREMENT让MySQL自动为我们生成递增的ID,插入数据时无需手动指定,非常省心。这是表设计的核心。name VARCHAR(50) NOT NULL:变长字符串,最多50个字符。NOT NULL约束表示这个字段必须填写,不能为NULL。对于关键信息(如姓名),一定要加NOT NULL。age TINYINT UNSIGNED:年龄用无符号小整数(0-255)存储足够,且不会出现负数。这里故意没加NOT NULL,意味着年龄可以是NULL(未知)。gender ENUM('男', '女') DEFAULT '男':枚举类型,值只能是‘男’或‘女’。DEFAULT ‘男’设置了默认值,如果插入时不指定性别,就会自动填‘男’。这能减少数据的不一致性。enrollment_date DATE NOT NULL:日期类型,存储年月日。score DECIMAL(5, 2):精确小数类型,(5,2)表示总共5位数字,其中小数点后占2位,因此整数部分最多3位(如999.99)。适合存储金额、分数等需要精确计算的数值。ENGINE=InnoDB:指定存储引擎为InnoDB。这是MySQL默认且最常用的引擎,支持事务、行级锁等关键特性,除非有特殊历史遗留原因,否则无脑选InnoDB。DEFAULT CHARSET=utf8mb4:设置默认字符集为utf8mb4。这是非常重要的一步!老的utf8字符集在MySQL中不支持完整的四字节UTF-8编码(如一些emoji表情),utf8mb4才是真正的全量UTF-8支持。现在创建表务必用这个,避免以后存特殊字符时报错。
执行完上述SQL后,你可以用DESC students;命令查看表结构,确认每个字段的类型和约束是否如你所愿。
3. 核心操作一:增(INSERT)—— 注入数据生命
“增”是数据操作的起点,是把业务实体转化为数据库记录的步骤。INSERT语句看似简单,但细节决定成败。
3.1 基础插入:指定列与全列插入
最清晰、最推荐的方式是指定列名插入,即使你有所有列的数据。这样做的好处是即使表结构后续增加新字段,你的旧插入语句也不会出错。
-- 方式1:指定列名插入(推荐) INSERT INTO students (name, age, gender, enrollment_date, score) VALUES ('张三', 20, '男', '2023-09-01', 89.5); -- 方式2:为所有列插入(需严格按表字段顺序) INSERT INTO students VALUES (NULL, '李四', 22, '女', '2022-09-01', 92.0);实操要点:
- 自增ID处理:在指定列名插入时,我们省略了
id,MySQL会自动分配下一个自增值。在全列插入时,必须用NULL或0(取决于SQL模式)来占位,告诉MySQL“这个字段请你自动生成”。 - 字符串与日期:必须用单引号
''包裹。 - 枚举值:必须使用定义时列举的值(‘男’或‘女’),大小写敏感。
- NULL值:对于允许为NULL的字段(如
age),如果想插入NULL,直接在VALUES中写NULL即可(不加引号)。
3.2 批量插入:提升效率的关键技巧
一次性插入多条数据比循环执行单条INSERT语句效率高几个数量级,因为它减少了网络通信和SQL解析的开销。
INSERT INTO students (name, age, gender, enrollment_date, score) VALUES ('王五', 21, '男', '2023-09-01', 85.0), ('赵六', 19, '女', '2024-03-01', NULL), ('孙七', 23, '男', '2021-09-01', 76.5);注意事项:批量插入时,如果其中一行数据违反约束(如重复主键),在默认情况下,整个批量插入操作会全部失败,所有行都不会被插入。这是事务原子性的体现。如果你希望忽略错误行继续插入,可以考虑使用INSERT IGNORE,但需谨慎,因为它会静默忽略所有错误(包括其他约束错误)。更精细的控制可以使用INSERT ... ON DUPLICATE KEY UPDATE,在遇到主键冲突时转为更新操作。
3.3 插入操作中的常见“坑”与排查
错误:
Data too long for column 'name'- 原因:插入的字符串长度超过了字段定义(如
VARCHAR(50))。 - 解决:检查输入数据,或考虑修改表结构增大字段长度。在应用层做长度校验是更好的实践。
- 原因:插入的字符串长度超过了字段定义(如
错误:
Incorrect integer value: 'abc' for column 'age'- 原因:给数值型字段传入了非数值字符串。
- 解决:确保应用层传递的数据类型与数据库字段类型匹配。在强类型语言(如Java)中,用好ORM框架的类型映射能避免此类问题。
错误:
Field 'name' doesn't have a default value- 原因:
name字段定义为NOT NULL,但你的INSERT语句既没有在列列表中包含它,也没有为其提供值。 - 解决:检查
INSERT语句的列列表和VALUES列表是否一一对应,且所有NOT NULL且无DEFAULT的字段都必须被赋值。
- 原因:
个人心得:在业务代码中,我强烈建议使用“参数化查询”或“预编译语句(PreparedStatement)”来执行
INSERT,而不是手动拼接SQL字符串。这不仅能防止SQL注入攻击,也能让数据库提前编译SQL执行计划,提升性能,并自动处理数据类型转换和特殊字符转义(如字符串中的单引号)。
4. 核心操作二:查(SELECT)—— 探索数据海洋
SELECT是你使用最频繁的语句,它的能力决定了你能从数据中挖掘出多少信息。我们由浅入深。
4.1 基础查询与过滤(WHERE)
先从最简单的获取全部数据开始,然后学习如何筛选。
-- 1. 查询所有列的所有行 SELECT * FROM students; -- 2. 只查询特定列 SELECT name, score FROM students; -- 3. 查询时过滤行(WHERE子句) SELECT * FROM students WHERE gender = '女'; SELECT * FROM students WHERE age > 20; SELECT * FROM students WHERE score IS NOT NULL; -- 查询成绩非空的学生 SELECT * FROM students WHERE enrollment_date >= '2023-01-01';WHERE子句操作符详解:
=,!=或<>:等于,不等于。>,<,>=,<=:比较数值或日期。BETWEEN ... AND ...:范围查询,包含边界。WHERE age BETWEEN 18 AND 22。IN (...):匹配列表中的任意值。WHERE gender IN ('男', '女')。LIKE:模糊匹配。%代表任意字符(包括零个),_代表一个字符。WHERE name LIKE '张%':找姓张的。WHERE name LIKE '%三':找名字以“三”结尾的。WHERE name LIKE '_三':找名字为两个字且以“三”结尾的(如“张三”)。
IS NULL/IS NOT NULL:判断是否为NULL。切记,不能用= NULL来判断!
4.2 结果排序(ORDER BY)与限制(LIMIT)
查询结果默认是按物理存储顺序返回的,这通常不可控。ORDER BY和LIMIT让你能精确控制返回什么。
-- 按成绩降序排列(从高到低) SELECT name, score FROM students WHERE score IS NOT NULL ORDER BY score DESC; -- 先按性别升序,同性别的再按年龄降序排列 SELECT * FROM students ORDER BY gender ASC, age DESC; -- 只获取成绩最高的前3名学生 SELECT name, score FROM students ORDER BY score DESC LIMIT 3; -- 分页查询的经典模式:LIMIT offset, count -- 获取第2页的数据,假设每页显示5条(即跳过前5条,取接下来的5条) SELECT * FROM students ORDER BY id LIMIT 5, 5;关键点:
DESC降序,ASC升序(默认)。LIMIT非常适合做分页,但LIMIT 100000, 10这种大偏移量查询性能极差,因为它需要先扫描并跳过前10万行。对于深度分页,通常需要配合WHERE条件(如WHERE id > 上一页最后一条的ID)来优化。
4.3 聚合函数与分组统计(GROUP BY)
当你想知道“有多少”、“平均值”、“总和”时,就需要聚合函数。
-- 基础聚合 SELECT COUNT(*) AS total_students, -- 总学生数 AVG(score) AS avg_score, -- 平均分(自动忽略NULL) MAX(score) AS max_score, -- 最高分 MIN(score) AS min_score, -- 最低分 SUM(score) AS total_score -- 总分 FROM students; -- 按性别分组统计 SELECT gender, COUNT(*) AS count, AVG(score) AS avg_score, AVG(age) AS avg_age FROM students WHERE score IS NOT NULL -- 先过滤掉没成绩的 GROUP BY gender; -- 按性别分组 -- 分组后过滤(HAVING子句) -- 查询平均分大于80的性别分组 SELECT gender, AVG(score) AS avg_score FROM students WHERE score IS NOT NULL GROUP BY gender HAVING avg_score > 80;WHEREvsHAVING核心区别(必考知识点):
WHERE:在分组前过滤行,作用于原始数据行。它不能使用聚合函数(如AVG(score))。HAVING:在分组后过滤组,作用于分组聚合后的结果集。它可以使用聚合函数和分组字段。
4.4 查询性能与索引初探
随着数据量增大,SELECT可能会变慢。一个简单的SELECT * FROM students WHERE name = ‘张三’;如果students表有百万行,它可能需要全表扫描。
解决方案:索引。你可以把索引理解为书本的目录。在上面的查询中,如果我们在name字段上创建索引,MySQL就能像查目录一样快速定位到‘张三’所在的数据页,而不是翻遍整本书。
-- 为name字段创建普通索引 CREATE INDEX idx_name ON students(name); -- 创建复合索引(常用于多条件查询) CREATE INDEX idx_gender_age ON students(gender, age);索引使用心得:
- 主键(PRIMARY KEY)和唯一键(UNIQUE KEY)会自动创建索引。
- 索引能极大加速
WHERE、ORDER BY、GROUP BY和JOIN操作。 - 但索引不是免费的,它会占用磁盘空间,并在数据
INSERT、UPDATE、DELETE时带来额外的维护开销。因此,不要为所有列都建索引。 - 通常,为高频查询条件、需要排序或分组的字段以及外键字段创建索引。
- 可以使用
EXPLAIN命令来分析你的SELECT语句是否用到了索引:EXPLAIN SELECT * FROM students WHERE name = ‘张三’;。查看结果中的key列,如果显示了idx_name,说明索引生效了。
5. 核心操作三:改(UPDATE)—— 修正数据轨迹
数据不是一成不变的,UPDATE用于修改已有记录。这是一把威力巨大的双刃剑,务必谨慎!
5.1 基础更新与条件更新
-- 1. 更新所有行(危险!通常很少用) -- 将所有人的年龄加1(生日到了) UPDATE students SET age = age + 1; -- 2. 带条件的更新(必须用WHERE!) -- 将张三的成绩改为90 UPDATE students SET score = 90.0 WHERE name = '张三'; -- 3. 同时更新多个字段 -- 李四年龄增长,同时更新入学日期(假设留级了) UPDATE students SET age = age + 1, enrollment_date = '2023-09-01' WHERE name = '李四';UPDATE的黄金法则:在执行前,先把UPDATE改成SELECT这是一个救命的习惯。在敲下UPDATE ... WHERE ...之前,先执行SELECT * FROM ... WHERE ...,看看WHERE条件筛选出的到底是不是你想修改的那几行数据。确认无误后,再把SELECT替换为UPDATE。我见过太多因为漏写WHERE条件或条件写错而导致的全表更新事故。
5.2 基于子查询的复杂更新
有时,新值需要从另一张表或其他复杂逻辑中计算得出。
-- 假设有一张score_adjustments(分数调整表),记录每个学生的加分 -- 更新students表,将成绩加上对应的调整分 UPDATE students s JOIN score_adjustments a ON s.id = a.student_id SET s.score = s.score + a.adjustment WHERE a.term = '2024春季';说明:这里使用了JOIN,将students表和score_adjustments表关联起来,然后为匹配上的行更新分数。这种操作在数据订正、批量计算时非常有用。
5.3 UPDATE操作的陷阱与事务安全
- 全表更新灾难:
UPDATE students SET score = 100;这条语句会更新表中所有行的成绩。永远、永远不要在生产环境不带WHERE条件执行UPDATE(和DELETE)! - 更新丢失(Lost Update):在高并发场景下,两个事务可能读取同一行数据,然后基于旧值计算并更新,导致后一个更新覆盖前一个。这需要通过数据库的锁机制(如行锁)或乐观锁(在表中增加版本号字段)来解决。
- 使用事务保证原子性:对于一组必须同时成功或同时失败的相关更新操作,要使用事务。
START TRANSACTION; -- 开始事务 UPDATE account SET balance = balance - 100 WHERE user_id = 1; -- A账户扣款 UPDATE account SET balance = balance + 100 WHERE user_id = 2; -- B账户收款 -- 此时可以检查业务逻辑,如果一切正常 COMMIT; -- 提交事务,更改永久生效 -- 如果中途出错 ROLLBACK; -- 回滚事务,所有更改撤销个人习惯:对于重要的后台数据订正脚本,我通常会把它写在一个事务里,先SELECT验证,再UPDATE,并在COMMIT前再次SELECT确认。同时,在操作前备份相关表(CREATE TABLE students_backup AS SELECT * FROM students;)也是一个好习惯。
6. 核心操作四:删(DELETE)—— 清理数据空间
DELETE操作是破坏性的,数据一旦删除,恢复起来非常困难(虽然可以从备份或Binlog恢复,但成本高)。因此,其谨慎程度应高于UPDATE。
6.1 条件删除与清空表
-- 1. 删除符合条件的行(务必带WHERE!) DELETE FROM students WHERE name = '孙七'; -- 2. 删除所有行(清空表,危险!) DELETE FROM students;DELETEvsTRUNCATE TABLE:两者都能清空表,但有本质区别:
DELETE FROM table_name;:逐行删除,会触发触发器(如果定义了的话),并且删除操作会记录在事务日志中,支持回滚。速度相对较慢。TRUNCATE TABLE table_name;:直接删除表的数据页并释放空间,相当于“重置”表。它不触发行级的DELETE触发器,日志记录方式不同,效率极高,且不能回滚(在某些数据库中可以,但MySQL中默认不行)。
选择建议:如果想快速清空一个大表且不需要回滚,用TRUNCATE。如果删除需要遵守业务逻辑(如触发级联删除、记录审计日志),或者只想删除部分数据,用DELETE。
6.2 关联删除与级联约束
有时,删除一张表的记录,需要同时删除另一张表中与之关联的记录。
-- 假设我们还有一张‘选课记录’表course_selections,其中student_id引用students.id -- 方式1:手动先删子表,再删父表(繁琐) DELETE FROM course_selections WHERE student_id = 100; DELETE FROM students WHERE id = 100; -- 方式2:利用外键的级联删除(需在创建外键时定义) -- 创建外键时加上 ON DELETE CASCADE ALTER TABLE course_selections ADD FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE; -- 设置后,删除students中的一条记录,course_selections中对应的所有记录会自动删除。级联删除的利弊:
- 利:保证了数据的一致性,避免了“孤儿记录”。
- 弊:破坏性大,可能误删大量关联数据,且操作不可逆。在设计数据库时,是否使用
ON DELETE CASCADE需要非常审慎的考虑。另一种常见做法是使用ON DELETE SET NULL(将外键设为NULL)或ON DELETE RESTRICT(禁止删除,除非先处理子表记录)。
6.3 删除操作的终极安全策略
- 软删除(Soft Delete)是首选:在实际业务中,极少进行物理删除。更通用的做法是给表增加一个
is_deleted(TINYINT,0表示未删除,1表示已删除)或deleted_at(TIMESTAMP,记录删除时间)字段。删除操作实际上只是UPDATE这个标志位。查询时默认加上WHERE is_deleted = 0。这保留了数据历史,便于审计和恢复。 - 备份先行:执行任何可能影响大量数据的
DELETE操作前,对目标表进行备份。 - 事务包裹:和
UPDATE一样,将DELETE放在事务中,先SELECT确认,再执行,可随时ROLLBACK。 - 权限控制:在生产数据库,
DELETE权限应该只授予极少数核心运维或DBA人员,开发人员账户不应拥有此权限。
7. 综合实战与复杂查询入门
掌握了四大基础操作后,我们通过几个稍微复杂一点的场景来串联运用它们。
7.1 场景:学生成绩管理模拟
假设我们需要完成一个任务:“找出2023年秋季入学、成绩高于平均分的学生,并将他们的信息导出,同时给这些学生的成绩统一加5分(不超过100分),最后清理掉已毕业(假设入学5年即毕业)的学生记录。”
-- 1. 查询:找出目标学生 SELECT id, name, score, enrollment_date FROM students WHERE YEAR(enrollment_date) = 2023 AND MONTH(enrollment_date) >= 9 -- 秋季入学,假设9月后 AND score > (SELECT AVG(score) FROM students WHERE score IS NOT NULL) -- 子查询计算平均分 ORDER BY score DESC; -- 2. 更新:给这些学生加分(使用子查询确定范围) UPDATE students s JOIN ( SELECT id FROM students WHERE YEAR(enrollment_date) = 2023 AND MONTH(enrollment_date) >= 9 AND score > (SELECT AVG(score) FROM students WHERE score IS NOT NULL) ) AS target ON s.id = target.id SET s.score = LEAST(s.score + 5, 100.00); -- LEAST函数确保分数不超过100 -- 3. 删除:清理已毕业学生(假设当前日期为2024年) -- 首先,非常建议先做一次查询确认 SELECT * FROM students WHERE enrollment_date <= DATE_SUB('2024-01-01', INTERVAL 5 YEAR); -- 确认无误后,再执行删除(这里我们采用软删除,假设有deleted_at字段) UPDATE students SET deleted_at = NOW() WHERE enrollment_date <= DATE_SUB('2024-01-01', INTERVAL 5 YEAR); -- 如果是物理删除(极度谨慎!) -- DELETE FROM students WHERE enrollment_date <= DATE_SUB('2024-01-01', INTERVAL 5 YEAR);这个例子融合了条件查询、子查询、关联更新和基于日期的计算。LEAST函数和DATE_SUB函数是处理业务边界条件的实用工具。
7.2 多表查询(JOIN)概念引入
真实业务中,数据分布在多张表里。比如,除了students表,还有courses(课程表)和selections(选课表)。想查询“张三选了哪些课”,就需要连接(JOIN)多张表。
-- 创建示例关联表 CREATE TABLE courses (id INT PRIMARY KEY, name VARCHAR(100)); CREATE TABLE selections (student_id INT, course_id INT, selected_date DATE); -- 内连接(INNER JOIN):只返回两表中能关联上的行 SELECT s.name AS student_name, c.name AS course_name, sel.selected_date FROM students s INNER JOIN selections sel ON s.id = sel.student_id INNER JOIN courses c ON sel.course_id = c.id WHERE s.name = '张三';JOIN是数据库查询的核心能力之一,除了INNER JOIN,还有LEFT JOIN(返回左表所有行,即使右表无匹配)、RIGHT JOIN、FULL OUTER JOIN等。理解它们之间的区别,是写出正确SQL的关键。
8. 常见错误、性能问题与排查心法
即使语法正确,操作也可能失败或效率低下。这里罗列一些典型问题。
8.1 语法与约束错误速查表
| 错误信息/现象 | 可能原因 | 解决方案 |
|---|---|---|
ERROR 1062 (23000): Duplicate entry ‘1’ for key ‘PRIMARY’ | 插入了重复的主键值。 | 检查主键字段值是否重复。如果是自增主键,通常不应手动指定值。 |
ERROR 1366 (HY000): Incorrect string value: ‘\xF0\x9F\x98\x8A’ for column ‘name’ | 插入了不支持的字符(如emoji)。 | 确保表、字段的字符集为utf8mb4,连接字符集也设置为utf8mb4。 |
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails | 插入或更新时,外键值在父表中不存在。 | 确保引用的值在父表中存在,或检查外键约束逻辑。 |
ERROR 1264 (22003): Out of range value for column ‘age’ | 插入的数值超出字段范围(如给TINYINT UNSIGNED赋值为300)。 | 检查输入值,或考虑修改字段类型为更大范围(如SMALLINT)。 |
UPDATE/DELETE 影响了意料之外的行数 | WHERE条件写得太宽或写错,导致匹配行数过多。 | 黄金法则:先SELECT,后UPDATE/DELETE。使用更精确的条件,如用主键id代替模糊的name。 |
查询速度非常慢 | 1. 数据量大。 2.WHERE条件字段无索引。 3. 使用了SELECT *且不需要所有列。 4. 联表查询写法不佳。 | 1. 为条件字段加索引。 2. 只查询需要的列。 3. 使用EXPLAIN分析查询计划。 4. 优化JOIN条件和子查询。 |
8.2 性能排查与优化入门
当你发现查询变慢时,可以按以下步骤初步排查:
- 使用
EXPLAIN:在慢查询的SELECT语句前加上EXPLAIN,查看执行计划。关注:type列:ALL表示全表扫描,最差;index表示全索引扫描;range/ref/eq_ref/const性能依次变好。key列:显示实际使用的索引。如果为NULL,说明没用到索引。rows列:MySQL预估需要扫描的行数,越小越好。
- 检查索引:
EXPLAIN显示没走索引?检查WHERE、ORDER BY、GROUP BY和JOIN ... ON后面的字段是否已创建索引。 - 避免
SELECT ***:只取需要的字段,减少网络传输和内存开销。 - 警惕
LIKE ‘%xxx%’:前导通配符%会导致索引失效。如果业务允许,尽量用LIKE ‘xxx%’。 - 优化分页:对于
LIMIT 100000, 20,考虑用WHERE id > 上一页最大ID LIMIT 20的方式。
8.3 日常维护习惯
- SQL语句格式化:写好格式化的SQL,便于阅读和排查。多使用换行和缩进。
- 注释:在复杂的业务SQL或脚本中,添加注释说明意图。
- 测试环境验证:任何写操作(INSERT/UPDATE/DELETE)和复杂的DDL(如加索引、改字段),先在测试环境执行验证。
- 备份意识:动生产数据前,心里要想着“这步错了能不能回滚?有没有备份?”。
- 了解你的数据:经常用
SELECT COUNT(*),DESC table_name等命令了解数据量和表结构,做到心中有数。
数据库操作,尤其是增删改查,是一门实践性极强的技能。从看懂到会写,从会写到写好,从写对到高效,每一步都需要大量的练习和踩坑。建议你在自己的练习库中,反复尝试本文中的例子,并尝试设计一些自己的业务场景来模拟。记住,安全、准确永远是第一位的,其次才是性能。当你对这些基础操作烂熟于心,并能下意识地考虑约束、索引和事务时,你就已经跨过了数据库入门最坚实的一道门槛。