SQL增删改查完整指南:INSERT/UPDATE/DELETE实战

前言

上篇讲了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多表查询,不想错过就关注一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Captain_Data

打赏一下~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值