2.14 数据删除(DELETE、TRUNCATE)
这一章我会带你彻底搞懂SQL中删除数据的两大利器:DELETE和TRUNCATE。学完之后,你能安全地清理无效订单、测试数据,并能区分什么时候用DELETE,什么时候用TRUNCATE。
学习前准备:
-
已完成MySQL安装(参考系列前几章)
-
已安装DBeaver或Navicat
-
准备一个练习数据库,比如
delete_demo
学习前环境准备
步骤1:确保MySQL服务已启动。
步骤2:创建练习数据库和表。
sql
CREATE DATABASE delete_demo
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE delete_demo;
-- 订单表
CREATE TABLE orders (
order_id VARCHAR(50) PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
order_status TINYINT NOT NULL DEFAULT 1 COMMENT '1待支付,2已支付,3已取消,4已完成',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 用户表
CREATE TABLE users (
user_id INT PRIMARY KEY,
user_name VARCHAR(50)
);
-- 插入测试数据
INSERT INTO users (user_id, user_name) VALUES (1, '张三'), (2, '李四'), (3, '王五');
INSERT INTO orders (order_id, user_id, amount, order_status, create_time) VALUES
('ORD001', 1, 299.00, 2, '2025-06-01 10:00:00'),
('ORD002', 2, 189.00, 1, '2025-06-01 11:00:00'),
('ORD003', 3, 599.00, 3, '2025-06-02 09:30:00'),
('ORD004', 1, 399.00, 2, '2025-06-03 14:20:00'),
('ORD005', 2, 99.00, 4, '2025-06-04 16:00:00');
DELETE与TRUNCATE的基础认知
3.1 核心定义
-
DELETE :逐行删除数据,支持
WHERE条件,可以回滚(在事务中)。删除速度慢,但更灵活。 -
TRUNCATE :清空整张表,不支持
WHERE条件,不能回滚(在MySQL中默认自动提交)。删除速度快,相当于DROP后重建表。
3.2 核心区别
| 对比维度 | DELETE | TRUNCATE |
|---|---|---|
| 是否支持WHERE | ✅ 支持 | ❌ 不支持(只能全表清空) |
| 是否可回滚 | ✅ 在事务中可以ROLLBACK | ❌ 默认自动提交,不可回滚 |
| 执行速度 | 慢(逐行操作) | 快(重建表) |
| 自增列计数 | 不重置,继续从上次最大值+1 | 重置为1 |
| 触发器触发 | 会触发DELETE触发器 | 不触发触发器 |
| 锁粒度 | 行级锁 | 表级锁 |
| 磁盘空间 | 不释放(只是标记删除) | 立即释放 |
3.3 电商场景下的适用选择
-
删除特定条件的行(如超时未支付订单) :用
DELETE。 -
清空临时表或测试表 :用
TRUNCATE。 -
删除大批量数据且需要释放磁盘空间 :用
TRUNCATE(如果全表清空)或分批DELETE后OPTIMIZE TABLE。
我的踩坑经历 :有一次我想清空一张临时表,用了
DELETE FROM temp_table,没有加WHERE。虽然删光了数据,但表占用的磁盘空间没释放,而且自增ID还在增长。后来改用TRUNCATE temp_table,空间释放了,ID也重置了。清空整张表,用TRUNCATE比DELETE更合适。
带WHERE条件的精准删除
4.1 基础语法
sql
DELETE FROM 表名 WHERE 条件;
4.2 电商实操案例
案例一:删除超时未支付的订单
电商规则:超过30分钟未支付的订单自动取消,需要从订单表中删除(或标记为取消)。这里演示物理删除。
sql
-- 删除30分钟前创建的待支付订单
DELETE FROM orders
WHERE order_status = 1 AND create_time < NOW() - INTERVAL 30 MINUTE;
案例二:删除指定用户的测试订单
sql
-- 删除用户ID为999的测试订单
DELETE FROM orders WHERE user_id = 999;
案例三:删除特定时间段内的已取消订单
sql
-- 删除2024年之前已取消的订单
DELETE FROM orders
WHERE order_status = 3 AND create_time < '2024-01-01';
4.3 安全操作黄金法则(再次强调)
执行DELETE前,必须先用相同的WHERE条件执行SELECT,确认要删除的行数正确。
sql
-- 第一步:查看要删除的数据
SELECT * FROM orders WHERE order_status = 3 AND create_time < '2024-01-01';
-- 第二步:执行删除
DELETE FROM orders WHERE order_status = 3 AND create_time < '2024-01-01';
-- 第三步:验证删除结果(可选)
SELECT COUNT(*) FROM orders WHERE order_status = 3 AND create_time < '2024-01-01'; -- 应为0
4.4 限制删除行数(LIMIT)
MySQL支持DELETE语句中使用LIMIT,限制删除的行数,分批删除可避免锁表过久。
sql
-- 每次只删除1000条超时订单
DELETE FROM orders WHERE order_status = 1 AND create_time < NOW() - INTERVAL 30 MINUTE LIMIT 1000;
可以循环执行直到影响行数为0。
实操避坑提醒 :
DELETE中如果使用了LIMIT,ORDER BY不是必须的,但为了可预测性,建议加上ORDER BY。另外,LIMIT在删除时可能造成"跳过"数据,如果表在并发写入,最好配合主键排序。
全表数据删除:DELETE无WHERE 与 TRUNCATE
5.1 DELETE无WHERE(全表删除)
sql
DELETE FROM orders;
- 特点:删除所有行,但表结构、索引、自增ID起始值保留(继续递增)。速度慢,支持事务回滚。
5.2 TRUNCATE(清空表)
sql
TRUNCATE TABLE orders;
- 特点:快速清空所有行,自增ID重置为1,释放磁盘空间,不支持回滚(在MySQL中默认提交)。
5.3 分步操作对比
使用DELETE清空测试表:
sql
START TRANSACTION;
DELETE FROM orders_test;
-- 检查结果,如果正确则COMMIT,否则ROLLBACK
COMMIT;
使用TRUNCATE清空测试表:
sql
TRUNCATE TABLE orders_test;
5.4 电商场景实操:清空临时表
大促期间,每小时会创建临时表orders_hour存储实时数据,处理完后需要清空。
sql
-- 推荐用TRUNCATE
TRUNCATE TABLE orders_hour;
5.5 避坑提醒
-
TRUNCATE不能回滚,执行前必须确认是测试环境或已备份。
-
TRUNCATE会重置自增ID,如果业务依赖ID连续性,慎用。
-
TRUNCATE不会触发DELETE触发器,如果表上有外键约束且子表有数据,可能无法执行(取决于外键设置)。
我的踩坑经历 :有一次我准备清空一张生产环境的临时表,用了
TRUNCATE,结果发现这张表有外键被其他表引用,执行失败。后来改用DELETE逐行删除才成功。TRUNCATE遇到外键约束时可能失败,需要先处理子表数据或临时禁用外键检查。
关联表删除
6.1 使用子查询删除
电商场景:删除所有没有关联用户的订单(孤儿订单)。
sql
DELETE FROM orders
WHERE user_id NOT IN (SELECT user_id FROM users);
注意 :MySQL中,DELETE的子查询不能直接引用被删除的表(某些版本会报错),可以用多表删除语法绕过。
6.2 使用多表JOIN删除(推荐)
语法:
sql
DELETE 别名1, 别名2 FROM 表1 别名1 JOIN 表2 别名2 ON 条件 WHERE 筛选;
电商场景一:删除用户及其所有订单(级联删除)
sql
-- 删除用户ID为1的用户及其所有订单
DELETE u, o FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id = 1;
电商场景二:删除没有订单的用户
sql
DELETE u FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.order_id IS NULL;
电商场景三:删除已取消订单中的无效商品明细(假设有订单明细表)
先创建订单明细表示例:
sql
CREATE TABLE order_items (
item_id INT PRIMARY KEY AUTO_INCREMENT,
order_id VARCHAR(50),
product_name VARCHAR(100),
FOREIGN KEY (order_id) REFERENCES orders(order_id)
);
INSERT INTO order_items (order_id, product_name) VALUES
('ORD003', '测试商品'),
('ORD003', '另一个商品');
删除已取消订单对应的明细:
sql
DELETE oi FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_status = 3;
6.3 分步操作
-
先用
SELECT验证关联结果。 -
将
SELECT改为DELETE,注意指定要删除的表别名。 -
执行并验证。
sql
-- 验证
SELECT oi.* FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_status = 3;
-- 删除
DELETE oi FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_status = 3;
实操避坑提醒 :多表删除时,
DELETE后面跟的是要删除的表的别名,不是*。如果要同时删除多表数据,用逗号分隔别名(如DELETE u, o FROM ...)。务必确认哪些表需要删除,避免误删。
综合实操案例:年度历史无效测试数据清理
7.1 案例背景
某服饰类目电商店铺需要进行数据清理,任务包括:
-
删除所有2023年之前创建的、状态为"已取消"的订单。
-
删除超过1年未登录的用户(假设有
last_login_time字段,这里简化用user_id不在新订单中出现)。 -
清空临时表
temp_order_import中的数据(使用TRUNCATE)。 -
删除没有关联订单的用户(孤儿用户)。
7.2 准备工作
添加必要的字段和测试数据。
sql
-- 添加last_login_time字段(模拟)
ALTER TABLE users ADD last_login_time DATETIME DEFAULT NOW();
UPDATE users SET last_login_time = '2023-01-01' WHERE user_id = 3; -- 模拟老用户
-- 创建临时表
CREATE TABLE temp_order_import LIKE orders;
INSERT INTO temp_order_import SELECT * FROM orders WHERE 1=0; -- 空表
7.3 分步操作
步骤1:删除2023年前的已取消订单
sql
-- 先查看
SELECT * FROM orders WHERE order_status = 3 AND create_time < '2023-01-01';
-- 删除
DELETE FROM orders WHERE order_status = 3 AND create_time < '2023-01-01';
步骤2:删除超过1年未登录的用户(假设条件:用户没有在最近一年的订单中出现)
sql
-- 查看孤儿用户
SELECT u.* FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.order_id IS NULL;
-- 删除
DELETE u FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
WHERE o.order_id IS NULL;
步骤3:清空临时表
sql
TRUNCATE TABLE temp_order_import;
步骤4:验证清理结果
sql
-- 检查订单表行数
SELECT COUNT(*) FROM orders;
-- 检查用户表行数
SELECT COUNT(*) FROM users;
7.4 合规提示
📌 电商数据合规红线:
- 删除用户数据前,必须确认符合数据保留政策。例如,用户注销后,根据《个人信息保护法》,数据保留不应超过必要期限。删除前应确认是否有未结订单或法律义务。
- 禁止物理删除生产核心表数据 。推荐采用"软删除"(增加
is_deleted字段标记),便于审计和恢复。- 删除操作必须记录日志:包括操作人、时间、删除条件、影响行数。重要删除需审批。
本章踩坑清单与合规总结
8.1 新手常见踩坑
| 错误 | 后果 | 正确做法 |
|---|---|---|
DELETE忘加WHERE |
全表数据丢失 | 先写WHERE,先SELECT验证 |
使用TRUNCATE删除子表有外键依赖 |
报错 | 先删除子表数据,或临时禁用外键检查 |
批量删除大表无LIMIT |
锁表过久,影响业务 | 分批删除(如每次1000行) |
多表删除时DELETE后写了* |
语法错误 | 写要删除的表别名 |
| 删除前不备份 | 误删无法恢复 | 备份:CREATE TABLE backup LIKE 原表; INSERT INTO backup SELECT * FROM 原表; |
8.2 安全删除最佳实践
-
永远先备份 :
CREATE TABLE orders_backup_20250401 AS SELECT * FROM orders WHERE 条件; -
开启事务 :
START TRANSACTION;→ 执行DELETE→SELECT验证 →COMMIT;或ROLLBACK; -
限制删除行数 :大表用
LIMIT分批删除,避免长事务。 -
检查外键依赖 :删除父表数据前,确认子表已处理或使用
ON DELETE CASCADE。 -
生产环境删除必须走审批。
8.3 电商数据合规提示
-
软删除优于硬删除 :在订单表、用户表中增加
is_deleted字段,默认0。查询时加上WHERE is_deleted = 0。这样数据可追溯,满足合规审计要求。 -
删除用户数据需满足"最小必要"原则:只删除不再需要的字段,而不是整条记录。
-
定期归档而非删除:历史数据可以移到归档库或冷存储,而不是直接物理删除。
结语
DELETE和TRUNCATE是数据生命周期管理的重要工具。掌握它们,你就能安全地清理无效数据、归档历史记录。但永远记住:删除操作不可逆,谨慎是唯一的安全带。
有问题的评论区留言,我看到会回复。