Appearance
04|增删改查
前两篇建好了库和表,结构定好了。但表是空的——没有数据。数据库的日常操作就是往里存数据(INSERT)、查出来(SELECT)、改(UPDATE)、删(DELETE),合称 CRUD。
这四个操作看着简单,但 UPDATE 和 DELETE 用错了是事故源头——少个 WHERE 条件就改了全表、删了全表。本篇讲基础 CRUD,重点讲怎么安全地改和删。
一、INSERT:存数据
往表里插一条:
sql
INSERT INTO servers (hostname, ip_addr, env, status, remark)
VALUES ('web-01', '10.0.0.11', 'prod', 1, 'nginx node');始终写清楚字段名。有种偷懒写法不写字段名:
sql
INSERT INTO servers VALUES ('web-01', '10.0.0.11', 'prod', 1, 'nginx node', ...);这依赖字段顺序,表结构一改(中间加列),数据会塞到错的列。写字段名是最简单的防护——列对列匹配,不怕顺序变。
一次插多行:
sql
INSERT INTO servers (hostname, ip_addr, env, status) VALUES
('web-02', '10.0.0.12', 'prod', 1),
('db-01', '10.0.0.21', 'prod', 1);比写多条 INSERT 快——一次事务提交。批量导数据用这种。
id 字段不用写——AUTO_INCREMENT 自动生成。created_at、updated_at 也不用写——有 DEFAULT CURRENT_TIMESTAMP。
二、SELECT:查数据
最基础的查询,指定要哪些字段、什么条件:
sql
SELECT id, hostname, ip_addr, env FROM servers
WHERE env = 'prod'
ORDER BY id DESC
LIMIT 10;WHERE 过滤、ORDER BY 排序、LIMIT 限制返回行数。查大表一定要 LIMIT,不然返回几十万行能把客户端和数据库都拖垮。
WHERE 的条件
等值、范围、组合:
sql
-- 等值
WHERE env = 'prod'
-- 范围
WHERE id BETWEEN 100 AND 200
WHERE created_at > '2026-06-01'
-- 组合
WHERE env = 'prod' AND status = 1
WHERE env = 'prod' OR env = 'staging'
-- IN 列表
WHERE env IN ('prod', 'staging')LIKE 模糊匹配
sql
WHERE hostname LIKE 'web-%'; -- 前缀固定,走索引
WHERE hostname LIKE '%web%'; -- 任意位置,不走索引LIKE 'web-%' 前缀固定能用索引加速。LIKE '%web%' 前面是通配符,BTree 索引没法定位,全表扫描。能固定前缀就固定前缀。
少用 SELECT *
sql
SELECT * FROM servers WHERE env = 'prod'; -- 别这么写
SELECT id, hostname, ip_addr FROM servers WHERE env = 'prod'; -- 写清楚要哪些SELECT * 取回所有字段,网络传输大、用不到的字段白取。表结构调整后程序可能受影响(多出个字段程序没处理)。写清楚需要的字段,也给读代码的人一个信号:这次查询关心这些数据。
三、UPDATE:改数据
改数据前先 SELECT 确认要改哪些行——这是防事故的关键习惯:
sql
-- 先查:要改哪些行
SELECT id, hostname, status FROM servers WHERE hostname = 'web-01';
-- 确认范围对,再改
UPDATE servers SET status = 0 WHERE hostname = 'web-01';sql
UPDATE servers SET status = 0;这会把整张表的 status 都改成 0。一行 WHERE 漏掉,全表被改,是不可逆的事故(除非有 binlog 能回放)。
安全更新保护
MySQL 有个开关防止无 WHERE 的误操作:
sql
SET SQL_SAFE_UPDATES = 1;开了之后,没有 key 条件或 LIMIT 的 UPDATE/DELETE 被拒绝。但它有局限——用不上索引的 WHERE 条件可能漏过去。生产操作前可以临时开,但不完全可靠,养成"先 SELECT 再改"的习惯才是根本。
大变更拆小批
改几十万行不适合一条 SQL 一次完成——锁持有时间长、事务大、可能卡住。拆成小批:
sql
UPDATE servers SET status = 0 WHERE id BETWEEN 1 AND 1000;
UPDATE servers SET status = 0 WHERE id BETWEEN 1001 AND 2000;每批几千行,控制锁持有时间。脚本里循环按 id 分批处理。
四、DELETE:删数据
删数据同样先 SELECT 确认范围:
sql
SELECT id, hostname FROM servers WHERE env = 'test';
-- 确认这些是要删的
DELETE FROM servers WHERE env = 'test';和 UPDATE 一样,无 WHERE 的 DELETE 删全表。
DELETE 和 TRUNCATE 的区别
sql
DELETE FROM servers WHERE env = 'test'; -- 按条件删
TRUNCATE TABLE servers; -- 清空整张表DELETE 按条件删行,可以加 WHERE,逐行删除、记 binlog(可恢复)。TRUNCATE 直接清空整表、重置自增 ID,不记 binlog(不可恢复),但比 DELETE 快很多。
要清空整张表且不要数据了,用 TRUNCATE。要按条件删,用 DELETE。TRUNCATE 不能加 WHERE,一旦执行表就空了,谨慎。
五、改和删的几条铁律
- 永远先 SELECT 确认范围——改和删前先查一遍要影响哪些行
- UPDATE/DELETE 必带 WHERE——没 WHERE 就是全表操作,几乎必然是事故
- 生产环境开 SQL_SAFE_UPDATES——多一层防护
- 大批量拆小批——别一条 SQL 改几十万行
- 删前备份——重要的表删前先
mysqldump导出一份,或记 binlog 位点
这几条不是形式主义,每一条都对应真实的事故。无 WHERE 的 UPDATE/DELETE 是最常见的数据库事故,养成先 SELECT 的习惯能挡掉大部分。
CRUD 之后
能存数据、查数据、改数据、删数据了。但前面查询都只是单表——现实里经常要关联多张表(查订单连带查用户信息)、要分组统计(每个环境多少台服务器)。下一篇讲查询进阶:JOIN、聚合、函数。