数据删除 #

一、DELETE基础 #

1.1 基本语法 #

sql
-- 基本删除语法
DELETE FROM table_name
WHERE condition;

-- 示例表
CREATE TABLE logs (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    action VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO logs (user_id, action) VALUES
    (1, 'login'),
    (1, 'view_page'),
    (2, 'login'),
    (2, 'purchase'),
    (3, 'login'),
    (1, 'logout'),
    (2, 'logout');

-- 删除特定记录
DELETE FROM logs WHERE id = 1;

-- 查看结果
SELECT * FROM logs;

1.2 WHERE子句 #

sql
-- 各种条件删除

-- 等于条件
DELETE FROM logs WHERE user_id = 3;

-- 范围条件
DELETE FROM logs 
WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);

-- IN 条件
DELETE FROM logs WHERE id IN (2, 3, 4);

-- LIKE 条件
DELETE FROM logs WHERE action LIKE '%view%';

-- 多条件组合
DELETE FROM logs 
WHERE user_id = 1 AND action = 'logout';

-- 子查询条件
DELETE FROM logs 
WHERE user_id IN (SELECT id FROM users WHERE status = 'inactive');

1.3 删除所有数据 #

sql
-- 危险操作: 删除所有数据
DELETE FROM logs;
-- 这会删除表中所有数据!

-- 更安全的做法: 使用 TRUNCATE
TRUNCATE TABLE logs;
-- 更快,但会重置自增ID

-- 或者使用事务确认
START TRANSACTION;
DELETE FROM logs WHERE 1=1;  -- 明确条件
SELECT COUNT(*) FROM logs;   -- 确认结果
-- ROLLBACK;  -- 如果误操作可以回滚
COMMIT;

二、安全删除 #

2.1 避免误删 #

sql
-- 危险: 没有 WHERE 子句
DELETE FROM logs;
-- 删除所有数据!

-- 安全做法1: 始终使用 WHERE 子句
DELETE FROM logs WHERE id = 999;

-- 安全做法2: 先查询后删除
SELECT * FROM logs WHERE user_id = 1;
-- 确认后删除
DELETE FROM logs WHERE user_id = 1;

-- 安全做法3: 使用 LIMIT
DELETE FROM logs WHERE user_id = 1 LIMIT 100;

-- 安全做法4: 使用事务
START TRANSACTION;
DELETE FROM logs WHERE user_id = 1;
SELECT COUNT(*) FROM logs WHERE user_id = 1;
-- 确认后提交
COMMIT;
-- 或回滚
-- ROLLBACK;

2.2 软删除 #

sql
-- 软删除: 不真正删除,只标记状态
ALTER TABLE logs ADD COLUMN deleted_at TIMESTAMP NULL;

-- 软删除操作
UPDATE logs 
SET deleted_at = NOW() 
WHERE id = 1;

-- 查询时过滤已删除数据
SELECT * FROM logs WHERE deleted_at IS NULL;

-- 真正删除时
DELETE FROM logs WHERE deleted_at IS NOT NULL;

2.3 外键约束 #

sql
-- 创建有外键关联的表
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    amount DECIMAL(10, 2),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE order_items (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id BIGINT,
    product_id BIGINT,
    quantity INT,
    FOREIGN KEY (order_id) REFERENCES orders(id)
);

-- 删除有外键关联的数据
-- 方法1: 先删除子表数据
DELETE FROM order_items WHERE order_id = 1;
DELETE FROM orders WHERE id = 1;

-- 方法2: 使用级联删除 (建表时指定)
CREATE TABLE order_items_cascade (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id BIGINT,
    product_id BIGINT,
    quantity INT,
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);

-- 删除订单时自动删除关联的订单项
DELETE FROM orders WHERE id = 1;

三、批量删除 #

3.1 使用LIMIT分批删除 #

sql
-- 大量数据分批删除
-- 每次删除 1000 条
DELETE FROM logs 
WHERE created_at < '2024-01-01'
LIMIT 1000;

-- 循环执行直到影响行数为 0
-- 可以在应用层循环调用

-- 使用存储过程批量删除
DELIMITER //
CREATE PROCEDURE batch_delete_logs(IN days INT)
BEGIN
    DECLARE affected INT DEFAULT 1;
    WHILE affected > 0 DO
        DELETE FROM logs 
        WHERE created_at < DATE_SUB(NOW(), INTERVAL days DAY)
        LIMIT 1000;
        SET affected = ROW_COUNT();
    END WHILE;
END //
DELIMITER ;

-- 调用存储过程
CALL batch_delete_logs(30);

3.2 按范围删除 #

sql
-- 按ID范围删除
DELETE FROM logs WHERE id BETWEEN 1 AND 10000;
DELETE FROM logs WHERE id BETWEEN 10001 AND 20000;

-- 按时间范围删除
DELETE FROM logs 
WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31';

-- 按分区删除 (如果表有分区)
ALTER TABLE logs DROP PARTITION p2023;

3.3 使用临时表 #

sql
-- 创建需要保留的数据临时表
CREATE TEMPORARY TABLE logs_to_keep AS
SELECT * FROM logs 
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY);

-- 清空原表
TRUNCATE TABLE logs;

-- 重新插入需要保留的数据
INSERT INTO logs 
SELECT * FROM logs_to_keep;

-- 删除临时表
DROP TEMPORARY TABLE logs_to_keep;

四、TRUNCATE语句 #

4.1 TRUNCATE vs DELETE #

text
TRUNCATE vs DELETE 对比
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   TRUNCATE:                                                 │
│   ├── 删除所有数据,保留表结构                             │
│   ├── 重置自增ID                                           │
│   ├── 不触发 DELETE 触发器                                 │
│   ├── 更快 (DDL操作)                                       │
│   ├── 不能回滚 (部分情况)                                  │
│   └── 不记录每行删除日志                                   │
│                                                             │
│   DELETE:                                                   │
│   ├── 可按条件删除                                         │
│   ├── 不重置自增ID                                         │
│   ├── 触发 DELETE 触发器                                   │
│   ├── 较慢 (DML操作)                                       │
│   ├── 可以回滚                                             │
│   └── 记录每行删除日志                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.2 TRUNCATE使用 #

sql
-- 清空表数据
TRUNCATE TABLE logs;

-- 效果等同于
DROP TABLE logs;
CREATE TABLE logs (...);  -- 重新创建

-- TRUNCATE 后自增ID重置
CREATE TABLE test (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

INSERT INTO test (name) VALUES ('a'), ('b'), ('c');
SELECT * FROM test;  -- id: 1, 2, 3

TRUNCATE TABLE test;
INSERT INTO test (name) VALUES ('d');
SELECT * FROM test;  -- id: 1 (重新开始)

五、多表删除 #

5.1 关联删除 #

sql
-- 创建关联表
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    status VARCHAR(20)
);

CREATE TABLE user_logs (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    action VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (name, status) VALUES
    ('Alice', 'active'),
    ('Bob', 'inactive'),
    ('Carol', 'deleted');

INSERT INTO user_logs (user_id, action) VALUES
    (1, 'login'), (2, 'login'), (3, 'login');

-- 关联删除 (删除不活跃用户的日志)
DELETE ul
FROM user_logs ul
JOIN users u ON ul.user_id = u.id
WHERE u.status = 'inactive';

-- 删除多个表的数据
DELETE u, ul
FROM users u
LEFT JOIN user_logs ul ON u.id = ul.user_id
WHERE u.status = 'deleted';

5.2 子查询删除 #

sql
-- 使用子查询条件删除
DELETE FROM user_logs 
WHERE user_id IN (
    SELECT id FROM users WHERE status = 'deleted'
);

-- 使用 EXISTS
DELETE FROM user_logs ul
WHERE EXISTS (
    SELECT 1 FROM users u 
    WHERE u.id = ul.user_id AND u.status = 'deleted'
);

六、ORDER BY和LIMIT #

6.1 按顺序删除 #

sql
-- 按时间排序删除最早的记录
DELETE FROM logs 
ORDER BY created_at ASC
LIMIT 1000;

-- 按ID排序删除
DELETE FROM logs 
ORDER BY id DESC
LIMIT 100;

-- 删除每个用户最早的日志
DELETE l1 FROM logs l1
LEFT JOIN (
    SELECT user_id, MIN(id) as min_id
    FROM logs
    GROUP BY user_id
) l2 ON l1.user_id = l2.user_id AND l1.id = l2.min_id
WHERE l2.min_id IS NOT NULL;

6.2 保留最新N条 #

sql
-- 删除旧数据,只保留每个用户最新的10条日志
DELETE FROM logs 
WHERE id NOT IN (
    SELECT id FROM (
        SELECT id FROM logs l2
        WHERE l2.user_id = logs.user_id
        ORDER BY created_at DESC
        LIMIT 10
    ) tmp
);

-- 或者使用窗口函数
DELETE FROM logs 
WHERE id IN (
    SELECT id FROM (
        SELECT id, 
               ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) as rn
        FROM logs
    ) ranked
    WHERE rn > 10
);

七、删除性能优化 #

7.1 性能建议 #

text
DELETE 性能优化建议
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   1. 使用索引条件                                           │
│   ├── WHERE 条件使用索引列                                 │
│   └── 避免全表扫描                                         │
│                                                             │
│   2. 分批删除                                               │
│   ├── 大量删除分批进行                                     │
│   └── 使用 LIMIT 控制批次大小                              │
│                                                             │
│   3. 非高峰期执行                                           │
│   ├── 大批量删除在低峰期执行                               │
│   └── 减少对业务的影响                                     │
│                                                             │
│   4. 考虑使用 TRUNCATE                                      │
│   ├── 清空表时使用 TRUNCATE                                │
│   └── 比 DELETE 快得多                                     │
│                                                             │
│   5. 删除前备份                                             │
│   ├── 重要数据先备份                                       │
│   └── 使用事务保护                                         │
│                                                             │
│   6. 监控删除进度                                           │
│   ├── 检查影响行数                                         │
│   └── 监控系统负载                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.2 查看执行计划 #

sql
-- 查看删除执行计划
EXPLAIN DELETE FROM logs WHERE user_id = 1;

-- 分析执行计划
EXPLAIN ANALYZE DELETE FROM logs WHERE user_id = 1;

八、数据恢复 #

8.1 使用事务回滚 #

sql
-- 事务保护
START TRANSACTION;

DELETE FROM logs WHERE user_id = 1;

-- 检查结果
SELECT COUNT(*) FROM logs WHERE user_id = 1;

-- 如果误删,可以回滚
ROLLBACK;

-- 确认无误后提交
-- COMMIT;

8.2 使用Flashback #

sql
-- TiDB 支持 Flashback 功能
-- 恢复被删除的表

FLASHBACK TABLE logs TO logs_recovered;

-- 恢复到指定时间点
FLASHBACK TABLE logs TO TIMESTAMP '2024-03-27 10:00:00';

8.3 使用备份恢复 #

bash
# 使用 BR 工具恢复数据
br restore table \
    --db mydb \
    --table logs \
    --storage "local:///backup/path" \
    --pd "pd:2379"

九、常见问题 #

9.1 删除超时 #

sql
-- 问题: 大量删除超时
-- Error: Lock wait timeout exceeded

-- 解决方案: 分批删除
DELETE FROM logs WHERE created_at < '2024-01-01' LIMIT 1000;
-- 循环执行

-- 或调整超时时间
SET SESSION innodb_lock_wait_timeout = 120;

9.2 磁盘空间不释放 #

sql
-- 问题: 删除数据后磁盘空间不释放

-- 原因: TiDB 使用 MVCC,删除只是标记
-- 需要等待 GC 回收

-- 查看 GC 状态
SELECT VARIABLE_NAME, VARIABLE_VALUE 
FROM mysql.tidb 
WHERE VARIABLE_NAME = 'tikv_gc_life_time';

-- 手动触发 GC (谨慎使用)
-- UPDATE mysql.tidb SET VARIABLE_VALUE = '1m' 
-- WHERE VARIABLE_NAME = 'tikv_gc_life_time';

9.3 删除导致性能下降 #

sql
-- 问题: 大量删除影响性能

-- 解决方案:
-- 1. 分批删除
DELETE FROM logs WHERE id <= 10000 LIMIT 1000;

-- 2. 低峰期执行

-- 3. 使用分区表,直接删除分区
ALTER TABLE logs DROP PARTITION p202301;

十、总结 #

DELETE 语句要点:

类型 说明
基本删除 WHERE条件、安全删除
批量删除 LIMIT分批、范围删除
TRUNCATE 清空表、重置ID
多表删除 JOIN删除、子查询
软删除 标记删除、保留数据
数据恢复 事务回滚、Flashback

下一步,让我们学习基础查询操作!

最后更新:2026-03-27