数据删除 #

一、DELETE基础 #

1.1 基本语法 #

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

-- 示例表
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100),
    status VARCHAR(20) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
);

INSERT INTO users (name, email, status) VALUES
    ('Alice', 'alice@example.com', 'active'),
    ('Bob', 'bob@example.com', 'inactive'),
    ('Carol', 'carol@example.com', 'deleted'),
    ('David', 'david@example.com', 'active');

-- 删除单行
DELETE FROM users
WHERE name = 'Alice';

-- 查看结果
SELECT * FROM users;

1.2 删除所有行 #

sql
-- 删除所有行 (慎用!)
DELETE FROM users;

-- 通常应该加 WHERE 条件
DELETE FROM users
WHERE status = 'deleted';

1.3 条件删除 #

sql
-- 等值条件
DELETE FROM users
WHERE email = 'alice@example.com';

-- 范围条件
DELETE FROM users
WHERE created_at < NOW() - INTERVAL '1 year';

-- 多条件组合
DELETE FROM users
WHERE status = 'deleted'
  AND created_at < NOW() - INTERVAL '30 days';

-- IN 条件
DELETE FROM users
WHERE id IN ('uuid-1', 'uuid-2', 'uuid-3');

-- NOT IN 条件
DELETE FROM users
WHERE status NOT IN ('active', 'pending');

二、TRUNCATE语句 #

2.1 TRUNCATE语法 #

sql
-- TRUNCATE 快速清空表
TRUNCATE TABLE users;

-- TRUNCATE vs DELETE
-- TRUNCATE: 更快,不触发触发器,重置序列
-- DELETE: 逐行删除,触发触发器,记录日志

-- TRUNCATE 多个表
TRUNCATE TABLE users, orders, logs;

-- TRUNCATE 与 CASCADE
TRUNCATE TABLE users CASCADE;  -- 同时删除依赖对象

2.2 TRUNCATE vs DELETE #

text
TRUNCATE vs DELETE 对比
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   TRUNCATE:                                                 │
│   ├── 速度快                                               │
│   ├── 不触发触发器                                         │
│   ├── 重置自增序列                                         │
│   ├── 不能回滚 (在某些数据库)                              │
│   └── 不记录每行删除日志                                   │
│                                                             │
│   DELETE:                                                   │
│   ├── 速度慢                                               │
│   ├── 触发触发器                                           │
│   ├── 不重置序列                                           │
│   ├── 可以回滚                                             │
│   └── 记录每行删除日志                                     │
│                                                             │
│   CockroachDB:                                              │
│   ├── TRUNCATE 和 DELETE 都可以回滚                        │
│   └── TRUNCATE 性能更好                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

三、批量删除 #

3.1 分批删除 #

sql
-- 分批删除大量数据
-- 每次删除 1000 行

-- 方法1: 使用 LIMIT
DELETE FROM logs
WHERE created_at < NOW() - INTERVAL '90 days'
LIMIT 1000;

-- 方法2: 使用事务
BEGIN;
DELETE FROM logs
WHERE created_at < NOW() - INTERVAL '90 days'
LIMIT 1000;
COMMIT;

-- 方法3: 循环删除
DO $$
DECLARE
    deleted_count INT;
BEGIN
    LOOP
        DELETE FROM logs
        WHERE created_at < NOW() - INTERVAL '90 days'
        LIMIT 1000;
        
        GET DIAGNOSTICS deleted_count = ROW_COUNT;
        
        EXIT WHEN deleted_count = 0;
        
        COMMIT;
    END LOOP;
END;
$$;

3.2 使用CTE删除 #

sql
-- 使用 CTE 删除
WITH to_delete AS (
    SELECT id
    FROM users
    WHERE status = 'deleted'
    LIMIT 1000
)
DELETE FROM users
WHERE id IN (SELECT id FROM to_delete);

-- 使用 CTE 删除并返回结果
WITH deleted AS (
    DELETE FROM users
    WHERE status = 'inactive'
    RETURNING *
)
SELECT COUNT(*) as deleted_count FROM deleted;

四、级联删除 #

4.1 外键级联 #

sql
-- 创建带外键的表
CREATE TABLE orders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    amount DECIMAL(10, 2),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE order_items (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
    product_name VARCHAR(100),
    quantity INT
);

-- 删除用户时自动删除相关订单和订单项
DELETE FROM users WHERE id = 'user-uuid';
-- 自动删除 orders 和 order_items 中的相关数据

4.2 手动级联删除 #

sql
-- 手动级联删除
BEGIN;

-- 先删除子表数据
DELETE FROM order_items
WHERE order_id IN (
    SELECT id FROM orders WHERE user_id = 'user-uuid'
);

-- 再删除父表数据
DELETE FROM orders WHERE user_id = 'user-uuid';

-- 最后删除主表数据
DELETE FROM users WHERE id = 'user-uuid';

COMMIT;

五、软删除 #

5.1 软删除实现 #

sql
-- 添加软删除字段
ALTER TABLE users
ADD COLUMN deleted_at TIMESTAMP DEFAULT NULL;

-- 软删除 (不真正删除,只标记)
UPDATE users
SET deleted_at = NOW()
WHERE id = 'user-uuid';

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

-- 创建视图简化查询
CREATE VIEW active_users AS
SELECT * FROM users
WHERE deleted_at IS NULL;

-- 使用视图查询
SELECT * FROM active_users;

5.2 软删除最佳实践 #

text
软删除最佳实践
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   优点:                                                     │
│   ├── 数据可恢复                                           │
│   ├── 保留历史记录                                         │
│   └── 支持审计追踪                                         │
│                                                             │
│   缺点:                                                     │
│   ├── 查询需要额外条件                                     │
│   ├── 索引需要包含删除标记                                 │
│   └── 数据量持续增长                                       │
│                                                             │
│   建议:                                                     │
│   ├── 使用 deleted_at 而不是 is_deleted                    │
│   ├── 创建过滤视图                                         │
│   ├── 定期归档已删除数据                                   │
│   └── 添加唯一索引时排除已删除数据                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.3 软删除索引 #

sql
-- 创建部分索引 (只索引未删除数据)
CREATE INDEX idx_users_email ON users(email)
WHERE deleted_at IS NULL;

-- 创建唯一索引 (排除已删除数据)
CREATE UNIQUE INDEX idx_users_email_unique ON users(email)
WHERE deleted_at IS NULL;

-- 查询时自动使用索引
SELECT * FROM users
WHERE email = 'test@example.com'
  AND deleted_at IS NULL;

六、RETURNING子句 #

6.1 返回删除结果 #

sql
-- 返回删除的行
DELETE FROM users
WHERE status = 'deleted'
RETURNING *;

-- 返回特定列
DELETE FROM users
WHERE id = 'user-uuid'
RETURNING id, name, email;

-- 返回计算结果
DELETE FROM users
WHERE created_at < NOW() - INTERVAL '1 year'
RETURNING 
    id,
    name,
    created_at,
    NOW() - created_at AS age;

6.2 使用返回结果 #

sql
-- 删除并归档
WITH deleted AS (
    DELETE FROM users
    WHERE status = 'deleted'
    RETURNING *
)
INSERT INTO users_archive
SELECT *, NOW() AS archived_at FROM deleted;

-- 删除并统计
WITH deleted AS (
    DELETE FROM logs
    WHERE created_at < NOW() - INTERVAL '90 days'
    RETURNING *
)
SELECT 
    COUNT(*) as deleted_count,
    MIN(created_at) as oldest_deleted,
    MAX(created_at) as newest_deleted
FROM deleted;

七、使用子查询删除 #

7.1 使用子查询条件 #

sql
-- 使用子查询删除
DELETE FROM users
WHERE id IN (
    SELECT user_id
    FROM orders
    WHERE created_at < NOW() - INTERVAL '1 year'
    GROUP BY user_id
    HAVING COUNT(*) = 0
);

-- 使用相关子查询
DELETE FROM users u
WHERE NOT EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.user_id = u.id
);

-- 使用 JOIN 删除
DELETE FROM users
USING orders
WHERE users.id = orders.user_id
  AND orders.status = 'cancelled';

7.2 删除重复数据 #

sql
-- 删除重复数据,保留最新的一条
DELETE FROM users u1
WHERE EXISTS (
    SELECT 1
    FROM users u2
    WHERE u2.email = u1.email
      AND u2.created_at > u1.created_at
);

-- 使用 CTE 删除重复数据
WITH duplicates AS (
    SELECT id,
           ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at DESC) as rn
    FROM users
)
DELETE FROM users
WHERE id IN (
    SELECT id FROM duplicates WHERE rn > 1
);

八、删除性能 #

8.1 性能建议 #

text
删除性能优化建议
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   1. 使用索引                                               │
│   ├── WHERE 条件使用索引列                                 │
│   └── 加快定位速度                                         │
│                                                             │
│   2. 分批删除                                               │
│   ├── 减少锁持有时间                                       │
│   └── 降低冲突概率                                         │
│                                                             │
│   3. 使用 TRUNCATE                                          │
│   ├── 清空整表时使用                                       │
│   └── 性能更好                                             │
│                                                             │
│   4. 考虑软删除                                             │
│   ├── 避免实际删除                                         │
│   └── 便于恢复                                             │
│                                                             │
│   5. 删除后维护                                             │
│   ├── 清理索引                                             │
│   └── 更新统计信息                                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

8.2 查看执行计划 #

sql
-- 查看删除执行计划
EXPLAIN DELETE FROM users
WHERE status = 'deleted';

-- 查看详细执行计划
EXPLAIN ANALYZE DELETE FROM users
WHERE status = 'deleted';

九、常见问题 #

9.1 外键约束错误 #

sql
-- 问题: 删除有外键引用的行
DELETE FROM users WHERE id = 'user-uuid';
-- Error: foreign key violation

-- 解决方案1: 先删除引用数据
DELETE FROM orders WHERE user_id = 'user-uuid';
DELETE FROM users WHERE id = 'user-uuid';

-- 解决方案2: 使用 CASCADE
DELETE FROM users WHERE id = 'user-uuid' CASCADE;

-- 解决方案3: 设置外键为 ON DELETE CASCADE
ALTER TABLE orders
DROP CONSTRAINT orders_user_id_fkey,
ADD CONSTRAINT orders_user_id_fkey
    FOREIGN KEY (user_id) REFERENCES users(id)
    ON DELETE CASCADE;

9.2 删除大量数据 #

sql
-- 问题: 删除大量数据导致性能问题
-- 解决: 分批删除

DO $$
DECLARE
    batch_size INT := 10000;
    total_deleted INT := 0;
    batch_deleted INT;
BEGIN
    LOOP
        DELETE FROM logs
        WHERE created_at < NOW() - INTERVAL '90 days'
        LIMIT batch_size;
        
        GET DIAGNOSTICS batch_deleted = ROW_COUNT;
        total_deleted := total_deleted + batch_deleted;
        
        EXIT WHEN batch_deleted = 0;
        
        COMMIT;
        RAISE NOTICE 'Deleted % rows, total: %', batch_deleted, total_deleted;
    END LOOP;
    
    RAISE NOTICE 'Total deleted: %', total_deleted;
END;
$$;

9.3 误删数据恢复 #

sql
-- 使用 AS OF SYSTEM TIME 恢复数据
-- 假设误删发生在 10 分钟前

-- 查看删除前的数据
SELECT * FROM users
AS OF SYSTEM TIME '-10m'
WHERE id = 'deleted-uuid';

-- 恢复数据
INSERT INTO users
SELECT * FROM users
AS OF SYSTEM TIME '-10m'
WHERE id = 'deleted-uuid';

十、总结 #

DELETE 语句要点:

类型 说明
基本删除 条件删除、批量删除
TRUNCATE 快速清空表
级联删除 外键级联、手动级联
软删除 标记删除、保留数据
RETURNING 返回删除结果

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

最后更新:2026-03-27