Skip to content

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_atupdated_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,一旦执行表就空了,谨慎。

五、改和删的几条铁律

  1. 永远先 SELECT 确认范围——改和删前先查一遍要影响哪些行
  2. UPDATE/DELETE 必带 WHERE——没 WHERE 就是全表操作,几乎必然是事故
  3. 生产环境开 SQL_SAFE_UPDATES——多一层防护
  4. 大批量拆小批——别一条 SQL 改几十万行
  5. 删前备份——重要的表删前先 mysqldump 导出一份,或记 binlog 位点

这几条不是形式主义,每一条都对应真实的事故。无 WHERE 的 UPDATE/DELETE 是最常见的数据库事故,养成先 SELECT 的习惯能挡掉大部分。

CRUD 之后

能存数据、查数据、改数据、删数据了。但前面查询都只是单表——现实里经常要关联多张表(查订单连带查用户信息)、要分组统计(每个环境多少台服务器)。下一篇讲查询进阶:JOIN、聚合、函数。