前言
上篇讲了SQL查询的10个核心语句,这篇讲SQL的另一半:数据操作。
查询(SELECT)是"看数据",而增删改(INSERT/UPDATE/DELETE)是"动数据"。
很多同学学了SELECT就以为自己会SQL了,结果一到实际工作中——录入数据不会INSERT,改错了不会UPDATE,删错了不会回滚。
今天这篇,把INSERT、UPDATE、DELETE三个语句讲透,外加事务控制,确保你不会因为误操作把数据库搞崩。
建议:先看上篇《SQL零基础入门:10个语句解决80%的查询问题》,再看这篇,效果翻倍。
〇、建表准备
和上篇一样,基于电商订单表来演示。如果上篇的表还在,这步可以跳过。
-- ============================================
-- 电商订单表 orders
-- 公主号:船长Talk
-- ============================================
CREATE TABLE orders (
order_id INT PRIMARY KEY COMMENT '订单ID',
customer VARCHAR(50) COMMENT '客户姓名',
product VARCHAR(100) COMMENT '商品名称',
category VARCHAR(50) COMMENT '商品分类',
price DECIMAL(10,2) COMMENT '单价',
quantity INT COMMENT '购买数量',
total_amount DECIMAL(10,2) COMMENT '订单总金额',
order_date DATE COMMENT '下单日期',
status VARCHAR(20) COMMENT '订单状态'
);
-- 插入初始测试数据
INSERT INTO orders VALUES
(1, '张三', 'iPhone 15', '手机', 7999.00, 1, 7999.00, '2026-04-01', '已发货'),
(2, '李四', 'MacBook Pro', '电脑', 14999.00, 1,14999.00, '2026-04-01', '已完成'),
(3, '张三', 'AirPods Pro', '配件', 999.00, 2, 1998.00, '2026-04-02', '已完成'),
(4, '王五', 'iPhone 15', '手机', 7999.00, 1, 7999.00, '2026-04-02', '待发货'),
(5, '赵六', 'iPad Air', '平板', 4799.00, 2, 9598.00, '2026-04-03', '已取消'),
(6, '李四', 'Apple Watch', '配件', 2999.00, 1, 2999.00, '2026-04-03', '已完成'),
(7, '张三', 'MacBook Pro', '电脑', 14999.00, 1,14999.00, '2026-04-04', '待发货'),
(8, '王五', 'Magic Keyboard', '配件', 1999.00, 1, 1999.00, '2026-04-05', '已发货'),
(9, '赵六', 'iPhone 15', '手机', 7999.00, 1, 7999.00, '2026-04-05', '已完成'),
(10, '张三', 'HomePod mini', '配件', 749.00, 2, 1498.00, '2026-04-06', '已完成');
一、INSERT —— 插入数据
**用途:**往表里新增一行或多行数据。这是所有数据操作的起点——没有INSERT,表就是空的。
1.1 基础插入:一次插入一行
-- ============================================
-- INSERT 基础用法
-- 公主号:船长Talk
-- ============================================
-- 最基本的插入(按列的顺序填值)
INSERT INTO orders
VALUES (11, '孙七', 'AirPods 3', '配件', 1399.00, 1, 1399.00, '2026-04-07', '待发货');
-- 指定列名插入(推荐!不依赖列的顺序,更安全)
INSERT INTO orders
(order_id, customer, product, category, price, quantity, total_amount, order_date, status)
VALUES
(12, '周八', 'iPad mini', '平板', 3799.00, 2, 7598.00, '2026-04-07', '已完成');
**实战建议:**永远用"指定列名"的方式插入。如果以后表加了新列,不指定列名的写法会直接报错。
1.2 批量插入:一次插入多行
-- 批量插入(效率远高于循环单条插入)
-- 场景:数据迁移、批量导入
INSERT INTO orders VALUES
(13, '孙七', 'Apple Pencil', '配件', 999.00, 2, 1998.00, '2026-04-08', '已完成'),
(14, '周八', 'iPhone 15', '手机', 7999.00, 1, 7999.00, '2026-04-08', '待发货'),
(15, '吴九', 'MacBook Air', '电脑', 8999.00, 1, 8999.00, '2026-04-08', '已发货');
**性能提示:**批量插入比循环单条INSERT快10-100倍。导入10万条数据,批量插入可能只要30秒,循环单条可能要10分钟。
1.3 插入查询结果:INSERT INTO ... SELECT
-- 把一个查询结果插入到另一张表
-- 场景:数据备份、报表归档
-- 先创建历史订单表(结构和orders一样)
CREATE TABLE orders_history LIKE orders;
-- 把已完成的订单归档到历史表
INSERT INTO orders_history
SELECT * FROM orders WHERE status = '已完成';
-- 验证:历史表里有多少条
SELECT COUNT(*) AS '归档订单数' FROM orders_history;
**实战场景:**每月把上月的已完成订单归档到历史表,主表只保留近3个月的数据,查询更快。
1.4 插入时处理冲突:ON DUPLICATE KEY UPDATE
-- MySQL特有语法:如果主键冲突,就更新而不是报错
-- 场景:用户积分系统——每天更新,没有就新增
-- 创建用户积分表
CREATE TABLE user_points (
user_id INT PRIMARY KEY COMMENT '用户ID',
points INT DEFAULT 0 COMMENT '积分余额',
level VARCHAR(20) DEFAULT '普通会员' COMMENT '会员等级',
updated_at DATE COMMENT '最后更新日期'
);
-- 第一次插入(不存在,直接新增)
INSERT INTO user_points (user_id, points, level, updated_at)
VALUES (1001, 500, '银牌会员', '2026-04-07')
ON DUPLICATE KEY UPDATE
points = points + VALUES(points),
level = CASE
WHEN points + VALUES(points) >= 5000 THEN '金牌会员'
WHEN points + VALUES(points) >= 1000 THEN '银牌会员'
ELSE '铜牌会员'
END,
updated_at = '2026-04-07';
-- 再执行一次(已存在,自动累加积分)
INSERT INTO user_points (user_id, points, level, updated_at)
VALUES (1001, 200, '银牌会员', '2026-04-08')
ON DUPLICATE KEY UPDATE
points = points + VALUES(points),
updated_at = '2026-04-08';
-- 查看结果:积分应该是 500 + 200 = 700
SELECT * FROM user_points WHERE user_id = 1001;
**核心价值:**不用先SELECT判断存不存在,再决定INSERT还是UPDATE。一条语句搞定,代码更简洁,也不会有并发问题。
二、UPDATE —— 修改数据
**用途:**修改表中已有的数据。这是工作中最危险的操作之一——忘了加WHERE,全表都会被改。
2.1 基础更新
-- ============================================
-- UPDATE 基础用法
-- 公主号:船长Talk
-- ============================================
-- 修改单个字段:把订单4的状态改为"已发货"
UPDATE orders SET status = '已发货' WHERE order_id = 4;
-- 修改多个字段:同时改状态和日期
UPDATE orders
SET status = '已完成',
order_date = '2026-04-08'
WHERE order_id = 4;
-- ⚠️ 危险操作演示(千万别在生产环境跑!)
-- 忘了加WHERE,所有订单的状态都会被改掉
-- UPDATE orders SET status = '已完成';
**铁律:**UPDATE语句必须有WHERE条件。写完UPDATE先看一眼有没有WHERE,没有就别执行。
2.2 条件更新:批量修改
-- 把所有"待发货"的订单改为"已发货"
UPDATE orders SET status = '已发货' WHERE status = '待发货';
-- 给所有手机品类的订单打9折
UPDATE orders
SET price = ROUND(price * 0.9, 2),
total_amount = ROUND(quantity * price * 0.9, 2)
WHERE category = '手机';
-- 给张三的所有已完成订单备注(需要有remark字段才能用)
-- 如果表没有remark字段,先加:ALTER TABLE orders ADD COLUMN remark VARCHAR(200);
ALTER TABLE orders ADD COLUMN remark VARCHAR(200) DEFAULT NULL;
UPDATE orders SET remark = 'VIP客户订单' WHERE customer = '张三' AND status = '已完成';
2.3 基于其他表的数据更新
-- 创建折扣表
CREATE TABLE discounts (
category VARCHAR(50) PRIMARY KEY COMMENT '品类',
rate DECIMAL(3,2) COMMENT '折扣率'
);
INSERT INTO discounts VALUES
('手机', 0.90),
('电脑', 0.95),
('配件', 0.85),
('平板', 0.92);
-- 用折扣表批量更新订单价格
UPDATE orders o
INNER JOIN discounts d ON o.category = d.category
SET o.price = ROUND(o.price * d.rate, 2),
o.total_amount = ROUND(o.quantity * o.price * d.rate, 2)
WHERE o.status = '待发货';
**实战场景:**运营部门给不同品类设置折扣,你用一条UPDATE批量应用,不用每个品类单独写。
2.4 UPDATE + CASE WHEN:复杂条件更新
-- 根据消费金额自动升级客户等级(需要在orders表加level字段)
ALTER TABLE orders ADD COLUMN customer_level VARCHAR(20) DEFAULT NULL;
-- 一次UPDATE,用CASE WHEN判断不同条件
UPDATE orders
SET customer_level = CASE
WHEN total_amount >= 10000 THEN 'A-大客户'
WHEN total_amount >= 5000 THEN 'B-中客户'
WHEN total_amount >= 1000 THEN 'C-小客户'
ELSE 'D-散客'
END;
-- 验证结果
SELECT customer_level, COUNT(*) AS '订单数', ROUND(AVG(total_amount),2) AS '平均金额'
FROM orders GROUP BY customer_level ORDER BY 平均金额 DESC;
三、DELETE —— 删除数据
用途:删除表中的数据。这是最危险的操作,没有之一。
**铁律:**DELETE之前先用SELECT查一遍确认范围,然后用事务包裹。
3.1 基础删除
-- ============================================
-- DELETE 基础用法
-- 公主号:船长Talk
-- ============================================
-- 删除指定行
DELETE FROM orders WHERE order_id = 15;
-- 删除满足条件的行:删除所有已取消的订单
DELETE FROM orders WHERE status = '已取消';
-- ⚠️ 危险操作!删除全表数据(千万别在生产环境跑!)
-- DELETE FROM orders; -- 没有WHERE = 删光所有数据
**安全习惯:**删除前先SELECT,确认数据范围。
-- 第一步:先看看要删哪些数据
SELECT * FROM orders WHERE status = '已取消';
-- 第二步:确认无误后再删
DELETE FROM orders WHERE status = '已取消';
3.2 DELETE vs TRUNCATE vs DROP
-- 三种"删除"的区别(非常重要)
-- 1. DELETE:逐行删除,记录日志,可以回滚(慢)
DELETE FROM orders WHERE status = '已取消';
-- 2. TRUNCATE:清空整张表,不记录日志,不可回滚(极快)
TRUNCATE TABLE orders_history;
-- 3. DROP:删除整张表(结构和数据都没了)
DROP TABLE IF EXISTS temp_table;
速查表:
操作
范围
WHERE条件
可回滚
速度
`DELETE`
部分行或全部
✅ 支持
✅ 事务内可回滚
慢(逐行删除)
`TRUNCATE`
整张表
❌ 不支持
❌ 不可回滚
极快(DDL操作)
`DROP`
整张表(含结构)
❌ 不支持
❌ 不可回滚
快
3.3 关联删除:基于其他表的数据删除
-- 删除在历史表中已归档的订单
-- 场景:主表清理,只保留最近活跃数据
DELETE o FROM orders o
INNER JOIN orders_history h ON o.order_id = h.order_id
WHERE h.status = '已完成' AND h.order_date 180;
-- 6. 自动升级等级
UPDATE user_points
SET level = CASE
WHEN points >= 10000 THEN '钻石会员'
WHEN points >= 5000 THEN '金牌会员'
WHEN points >= 1000 THEN '银牌会员'
WHEN points >= 100 THEN '铜牌会员'
ELSE '普通会员'
END;
-- 全部成功,提交
COMMIT;
-- 7. 查看最终结果
SELECT user_id, username, points, level, updated_at
FROM user_points
ORDER BY points DESC;
六、安全操作清单
在正式操作数据之前,对照这个清单检查一遍:
操作
安全检查项
INSERT
是否指定了列名?数据类型是否匹配?有没有主键冲突?
UPDATE
有没有WHERE?先用SELECT确认范围?是否在事务内?
DELETE
有没有WHERE?先用SELECT确认范围?是否在事务内?是否需要备份?
事务
START TRANSACTION写了?操作验证了?COMMIT/ROLLBACK别忘记?
写在最后
INSERT/UPDATE/DELETE + 事务控制,这是SQL数据操作的完整闭环。
和上篇的查询语句组合起来,你已经掌握了SQL的全部基础操作:
-
查询:SELECT / WHERE / ORDER BY / GROUP BY / HAVING(上篇)
-
写入:INSERT(本篇)
-
修改:UPDATE(本篇)
-
删除:DELETE(本篇)
-
安全:事务控制(本篇)
下一步:
-
进阶学习:JOIN多表查询(下篇更新)
-
面试准备:SQL面试50题(关注公主号,持续更新)
-
完整代码:后台私信"sql 代码",获取建表SQL + 全部练习代码
-- 公主号:船长Talk
-- 更多数据分析干货(SQL/Python/机器学习),持续更新
-- 有问题欢迎评论区留言,船长看到都会回复
觉得有用的话,点赞收藏,转给身边学SQL的朋友。下一篇讲JOIN多表查询,不想错过就关注一下。