DML 数据操作
DML(Data Manipulation Language,数据操作语言)负责把真正的数据塞进表里、改它、删它。掌握 DML 你就能完成"增删改"这三大日常操作。但 DML 也是最容易出事的地方——一行 SQL 写错可能误删全表,务必养成安全习惯。
1. INSERT 插入数据
建好表后,第一步就是把数据塞进去。INSERT INTO 是入口:
-- INSERT INTO 是 DML 里最简单的,往表里塞一行
-- 形式 1:列出列名 + 对应值(推荐,语义明确)
INSERT INTO students (name, age, email)
VALUES ('小明', 20, 'xm@example.com');
-- 形式 2:省略列名,顺序必须和表结构完全一致(不推荐!)
INSERT INTO students VALUES (NULL, '小明', 20, 'xm@example.com', 1, NULL);
-- ⚠️ 第二种写法的危险:
-- 1. 表加列后,语句立刻失败
-- 2. 列顺序记错,数据就错位
-- 永远用形式 1,显式列出列名几个细节:字符串用单引号 '小明',不是双引号;数字不用引号;NULL 是 SQL 里的"空值",表示"未知 / 没填",和 0、空字符串都不同。一个建议:永远用形式 1(显式列名),省心又防错。
2. 批量插入(性能利器)
循环里一条条 INSERT 是新手常犯的性能错误。一条 SQL 插入多行,效率能高 10 倍以上:
-- 一次插入多行(MySQL/PostgreSQL/SQLite 都支持)
INSERT INTO students (name, age, email) VALUES
('小明', 20, 'xm@example.com'),
('小红', 22, 'xh@example.com'),
('小刚', 21, 'xg@example.com'),
('小华', 23, 'xh2@example.com');
-- 比循环单条插入快 10 倍以上(网络往返少、事务开销小)
-- 数据导入、初始化数据时必用
-- INSERT IGNORE:遇到唯一约束冲突时跳过而不是报错(MySQL)
INSERT IGNORE INTO students (email, name) VALUES ('xm@example.com', '小明');
-- 如果 email 已存在,这条会被忽略,不报错
-- INSERT ... ON DUPLICATE KEY UPDATE:冲突就更新(MySQL 扩展)
INSERT INTO students (id, name, age) VALUES (1, '小明', 21)
ON DUPLICATE KEY UPDATE age = 21;批量插入省的是网络往返和事务开销——每条 INSERT 都要走一次完整的事务提交流程,而批量只走一次。生产环境插入大量数据时,推荐用批量 INSERT(每批 500-1000 行),或者用数据库的 LOAD DATA INFILE(更快)。
3. INSERT ... SELECT 数据迁移
有时你不是手动塞数据,而是从别的表搬。这时用 INSERT + SELECT 组合拳:
-- 把一张表的查询结果直接插入另一张表
INSERT INTO students_archive (name, age, email)
SELECT name, age, email FROM students WHERE graduated = 1;
-- 常见场景:
-- 1. 数据迁移:旧表搬到新表
-- 2. 数据归档:把历史数据搬到归档表
-- 3. 数据备份:操作前先备份一份
-- 配合 CREATE TABLE ... AS SELECT 一键复制表结构和数据
CREATE TABLE students_backup AS
SELECT * FROM students;这个写法在数据备份、归档、迁移场景天天用。比如要清理 5 年前的日志,先 INSERT 到归档表,确认数据完整,再 DELETE 原表。
2 类 INSERT 进阶
INSERT IGNORE:遇到唯一约束冲突(如重复 email)跳过而不报错。INSERT ... ON DUPLICATE KEY UPDATE(MySQL):冲突时改用 UPDATE。这就是著名的"upsert"模式,非常实用。
4. UPDATE 更新数据
改数据用 UPDATE ... SET ... WHERE ...。语句结构很简单,但陷阱深:
-- UPDATE 修改已有数据
-- 基本形式:UPDATE 表名 SET 列=值 WHERE 条件
UPDATE students SET age = 21 WHERE name = '小明';
-- 同时改多个字段(用逗号分隔,不是 AND!)
UPDATE students
SET age = 22, email = 'new@example.com', class_id = 2
WHERE id = 1;
-- 用表达式更新
UPDATE products SET price = price * 1.1; -- 所有商品涨价 10%
UPDATE products SET stock = stock - 1 WHERE id = 100; -- 库存减 1
-- ⚠️ 致命警告:忘了 WHERE 会改全表!!!
UPDATE students SET age = 21;
-- 这会把 students 表里【所有】学生的 age 都改成 21
-- 生产环境血泪教训一抓一大把,务必带 WHERE最关键的一句:UPDATE 必须带 WHERE,否则会改全表!MySQL 默认是 autocommit 模式,执行完立刻生效、无法回滚,所以一旦漏 WHERE,数据就被全表覆盖了。我见过有人凌晨误操作 UPDATE user SET password = ... 不带 WHERE,几百万用户密码被同步重置,直接事故。
5. DELETE 删除数据
DELETE 和 UPDATE 一样危险,而且删除是不可逆的(除非有备份或事务):
-- DELETE 删除行
DELETE FROM students WHERE id = 5;
DELETE FROM students WHERE age < 18 AND status = 'inactive';
-- 同样:忘了 WHERE 会删全表!!!
DELETE FROM students; -- 删掉所有数据(但表结构和自增 ID 保留)
-- DELETE vs TRUNCATE vs DROP 的区别:
-- DELETE FROM table 按条件删,可回滚,不重置自增ID
-- TRUNCATE TABLE table 清空所有数据,不可回滚,重置自增ID,更快
-- DROP TABLE table 删除表本身(数据+结构)
-- 删前先备份:把要删的数据 INSERT 到归档表
INSERT INTO students_deleted
SELECT * FROM students WHERE graduated_year < 2010;
DELETE FROM students WHERE graduated_year < 2010;DELETE、TRUNCATE、DROP 三个容易混。DELETE 按条件删、可回滚;TRUNCATE 清空表、不可回滚、重置自增 ID、更快;DROP 直接把表本身删掉(结构 + 数据都没)。删数据前先想清楚用哪个。
6. 三个救命习惯
误删数据是后端工程师的噩梦。下面三个习惯能救命:
-- ⚠️ 写 UPDATE/DELETE 前的 3 个救命习惯
-- 1. 先 SELECT 验证条件,看影响哪些行
SELECT * FROM students WHERE age < 18;
-- 确认返回的就是要删的,然后再:
DELETE FROM students WHERE age < 18;
-- 2. 重要操作用事务包起来
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
-- 检查无误后:
COMMIT;
-- 发现错了:
-- ROLLBACK; -- 撤销上面两条 UPDATE
-- 3. UPDATE/DELETE 加 LIMIT 限制影响行数(防误伤)
DELETE FROM logs WHERE created_at < '2020-01-01' LIMIT 1000;
-- 每次最多删 1000 行,分批删避免大事务卡库这三条是真实项目里的强制规范。写 UPDATE/DELETE 前先 SELECT 一遍、用事务包裹、加 LIMIT 分批,基本能挡住 99% 的误操作。
小结
DML 三剑客 INSERT / UPDATE / DELETE 你都见过了。到此为止,你能完整地建表、塞数据、改数据、删数据——CRUD 四件套齐了。下一篇我们看 SELECT 的真正威力:WHERE 条件过滤。
← 上一篇 DDL 数据定义
下一篇 WHERE 与排序 →