前言
上篇讲了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 < '2026-03-01';
四、事务控制 —— 安全网
用途:把多个SQL操作绑定为"一个整体",要么全部成功,要么全部回滚。
这是保护数据安全的最后一道防线。不会用事务,就不要在生产环境碰UPDATE和DELETE。
4.1 事务基础
-- ============================================
-- 事务基础
-- 公主号:船长Talk
-- ============================================
-- 事务四个关键字:START TRANSACTION / COMMIT / ROLLBACK / SAVEPOINT
-- 场景:张三买了一个MacBook,需要同时更新订单状态和扣减库存
START TRANSACTION; -- 开启事务
-- 操作1:更新订单状态
UPDATE orders SET status = '已发货' WHERE order_id = 7;
-- 操作2:扣减库存(假设有inventory表)
-- CREATE TABLE inventory (product VARCHAR(100), stock INT);
-- INSERT INTO inventory VALUES ('MacBook Pro', 50);
-- UPDATE inventory SET stock = stock - 1 WHERE product = 'MacBook Pro';
-- 如果一切正常,提交事务
COMMIT;
-- 如果出错了,回滚(撤销所有操作)
-- ROLLBACK;
4.2 实战:带事务的批量操作
-- 场景:月末批量归档 + 清理
-- 要求:归档和清理必须同时成功或同时失败
START TRANSACTION;
-- 1. 把已完成订单复制到历史表
INSERT INTO orders_history
SELECT * FROM orders WHERE status = '已完成' AND order_date < '2026-04-01';
-- 2. 从主表删除已归档的订单
DELETE FROM orders WHERE status = '已完成' AND order_date < '2026-04-01';
-- 3. 验证:检查归档数量是否正确
-- (这里用SELECT不会影响事务,只是检查)
-- 确认无误,提交
COMMIT;
-- 如果归档数量不对,回滚
-- ROLLBACK;
4.3 SAVEPOINT:部分回滚
-- 场景:批量更新订单状态,中间某一步出错了
START TRANSACTION;
-- 更新第一批:待发货 → 已发货
UPDATE orders SET status = '已发货' WHERE status = '待发货';
SAVEPOINT sp1; -- 设置保存点
-- 更新第二批:已发货 → 已完成(假设这步出错了)
UPDATE orders SET status = '已完成' WHERE status = '已发货' AND order_date < '2026-04-01';
SAVEPOINT sp2;
-- 如果第二步有问题,只回滚到sp1(保留第一步的结果)
-- ROLLBACK TO sp1;
-- 全部OK,提交
COMMIT;
五、综合实战:用户积分系统维护
把INSERT/UPDATE/DELETE/事务全部串起来,做一个完整的用户积分维护场景。
-- ============================================
-- 综合实战:用户积分系统
-- 公主号:船长Talk
-- ============================================
-- 1. 建表
CREATE TABLE IF NOT EXISTS user_points (
user_id INT PRIMARY KEY COMMENT '用户ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
points INT DEFAULT 0 COMMENT '积分余额',
level VARCHAR(20) DEFAULT '普通会员' COMMENT '等级',
created_at DATE COMMENT '注册日期',
updated_at DATE COMMENT '最后活跃日期'
);
-- 2. 初始化数据
INSERT INTO user_points VALUES
(1001, '张三', 3200, '银牌会员', '2025-06-15', '2026-04-08'),
(1002, '李四', 8700, '金牌会员', '2024-11-20', '2026-04-08'),
(1003, '王五', 800, '铜牌会员', '2026-01-10', '2026-04-07'),
(1004, '赵六', 50, '普通会员', '2026-04-01', '2026-04-06');
-- 3. 每日积分签到(有就更新,没有就插入)
INSERT INTO user_points (user_id, username, points, created_at, updated_at)
VALUES (1005, '孙七', 10, '2026-04-08', '2026-04-08')
ON DUPLICATE KEY UPDATE
points = points + 10,
updated_at = '2026-04-08';
-- 4. 消费送积分(每消费100元送10积分)
START TRANSACTION;
UPDATE user_points
SET points = points + FLOOR(7999 / 100) * 10, -- iPhone消费,得790积分
updated_at = '2026-04-08'
WHERE user_id = 1001;
-- 5. 积分过期清理(超过180天未活跃的用户,积分清零)
DELETE FROM user_points
WHERE DATEDIFF('2026-04-08', updated_at) > 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多表查询,不想错过就关注一下。

1691

被折叠的 条评论
为什么被折叠?



