PostgreSQL 数据删除 #
删除概述 #
PostgreSQL 提供两种删除数据的方式:DELETE 和 TRUNCATE。
text
┌─────────────────────────────────────────────────────────────┐
│ 删除方式对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ DELETE │
│ ├── 可以带 WHERE 条件 │
│ ├── 触发触发器 │
│ ├── 返回被删除的行 │
│ ├── 较慢(逐行删除) │
│ └── 可以回滚 │
│ │
│ TRUNCATE │
│ ├── 删除表中所有数据 │
│ ├── 不触发触发器 │
│ ├── 更快(一次性清空) │
│ ├── 重置自增序列 │
│ └── 可以回滚(在事务中) │
│ │
└─────────────────────────────────────────────────────────────┘
DELETE 语句 #
基本语法 #
sql
DELETE FROM table_name
[ WHERE condition ]
[ RETURNING * | column1, column2, ... ];
删除所有数据 #
sql
-- 创建示例表
CREATE TABLE logs (
id SERIAL PRIMARY KEY,
message TEXT,
level VARCHAR(10),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO logs (message, level) VALUES
('Error 1', 'ERROR'),
('Warning 1', 'WARN'),
('Info 1', 'INFO'),
('Error 2', 'ERROR');
-- 删除所有数据(危险操作!)
DELETE FROM logs;
-- 注意:不带 WHERE 条件会删除所有行
条件删除 #
sql
-- 根据条件删除
DELETE FROM logs WHERE level = 'ERROR';
-- 使用多个条件
DELETE FROM logs
WHERE level = 'WARN'
AND created_at < CURRENT_DATE;
-- 使用 IN
DELETE FROM logs
WHERE level IN ('DEBUG', 'TRACE');
-- 使用 BETWEEN
DELETE FROM logs
WHERE created_at BETWEEN '2025-01-01' AND '2025-12-31';
-- 使用 LIKE
DELETE FROM logs
WHERE message LIKE '%deprecated%';
-- 使用子查询
DELETE FROM logs
WHERE id IN (
SELECT id FROM logs
WHERE level = 'DEBUG'
LIMIT 100
);
RETURNING 子句 #
sql
-- 返回被删除的行
DELETE FROM logs
WHERE level = 'ERROR'
RETURNING *;
-- 输出:
-- id | message | level | created_at
-- ----+----------+-------+----------------------------
-- 1 | Error 1 | ERROR | 2026-03-29 10:00:00.000000
-- 4 | Error 2 | ERROR | 2026-03-29 10:00:00.000000
-- 返回特定列
DELETE FROM logs
WHERE id = 1
RETURNING id, message;
-- 返回计算结果
DELETE FROM logs
WHERE level = 'ERROR'
RETURNING id, message,
EXTRACT(DAY FROM CURRENT_TIMESTAMP - created_at) AS days_old;
使用子查询删除 #
sql
-- 创建关联表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
status VARCHAR(20)
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
amount DECIMAL(10, 2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, status) VALUES
('Alice', 'active'),
('Bob', 'inactive'),
('Charlie', 'inactive');
INSERT INTO orders (user_id, amount) VALUES
(1, 100.00),
(2, 50.00),
(3, 75.00);
-- 删除没有订单的用户
DELETE FROM users
WHERE id NOT IN (SELECT DISTINCT user_id FROM orders);
-- 删除订单金额小于平均值的订单
DELETE FROM orders
WHERE amount < (SELECT AVG(amount) FROM orders);
-- 使用 EXISTS
DELETE FROM users
WHERE NOT EXISTS (
SELECT 1 FROM orders WHERE orders.user_id = users.id
);
使用 USING 子句 #
sql
-- PostgreSQL 特有的 USING 语法
-- 删除特定用户的订单
DELETE FROM orders
USING users
WHERE orders.user_id = users.id
AND users.status = 'inactive';
-- 多表关联删除
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES orders(id),
product_name VARCHAR(100)
);
INSERT INTO order_items (order_id, product_name) VALUES
(1, 'Product A'),
(1, 'Product B'),
(2, 'Product C');
-- 删除特定用户的所有订单项
DELETE FROM order_items
USING orders, users
WHERE order_items.order_id = orders.id
AND orders.user_id = users.id
AND users.status = 'inactive';
TRUNCATE 语句 #
基本语法 #
sql
TRUNCATE [ TABLE ] table_name [ , ... ]
[ RESTART IDENTITY | CONTINUE IDENTITY ]
[ CASCADE | RESTRICT ];
清空表数据 #
sql
-- 清空单个表
TRUNCATE logs;
-- 清空多个表
TRUNCATE logs, temp_data;
-- 清空并重置自增序列
TRUNCATE logs RESTART IDENTITY;
-- 清空并级联删除关联表数据
TRUNCATE users CASCADE;
TRUNCATE 参数 #
text
┌─────────────────┬─────────────────────────────────────────────┐
│ 参数 │ 说明 │
├─────────────────┼─────────────────────────────────────────────┤
│ RESTART IDENTITY│ 重置自增序列到初始值 │
│ CONTINUE IDENTITY│ 保持序列当前值(默认) │
│ CASCADE │ 级联清空有外键引用的表 │
│ RESTRICT │ 如果有外键引用则报错(默认) │
└─────────────────┴─────────────────────────────────────────────┘
TRUNCATE vs DELETE #
sql
-- 性能对比
-- DELETE:逐行删除,记录日志,触发触发器
DELETE FROM large_table; -- 较慢
-- TRUNCATE:一次性清空,更快
TRUNCATE large_table; -- 更快
-- 具体区别
┌─────────────────┬────────────────────┬─────────────────────┐
│ 特性 │ DELETE │ TRUNCATE │
├─────────────────┼────────────────────┼─────────────────────┤
│ WHERE 条件 │ 支持 │ 不支持 │
│ 触发器 │ 触发 │ 不触发 │
│ RETURNING │ 支持 │ 不支持 │
│ 速度 │ 较慢 │ 很快 │
│ 序列重置 │ 不重置 │ 可重置 │
│ 事务回滚 │ 支持 │ 支持 │
│ VACUUM │ 需要手动 │ 不需要 │
└─────────────────┴────────────────────┴─────────────────────┘
级联删除 #
外键级联删除 #
sql
-- 创建带级联删除的表
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
category_id INTEGER REFERENCES categories(id) ON DELETE CASCADE
);
INSERT INTO categories (name) VALUES ('Electronics'), ('Books');
INSERT INTO products (name, category_id) VALUES
('Laptop', 1),
('Phone', 1),
('Novel', 2);
-- 删除分类时,关联的产品也会被删除
DELETE FROM categories WHERE id = 1;
-- 产品 Laptop 和 Phone 也会被删除
外键删除行为 #
sql
-- ON DELETE 选项
-- CASCADE: 级联删除
-- SET NULL: 设置为 NULL
-- SET DEFAULT: 设置为默认值
-- RESTRICT: 限制删除(默认)
-- NO ACTION: 同 RESTRICT
-- SET NULL 示例
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
amount DECIMAL(10, 2)
);
-- 删除用户时,订单的 user_id 会被设为 NULL
DELETE FROM users WHERE id = 1;
手动级联删除 #
sql
-- 先删除关联数据,再删除主数据
BEGIN;
DELETE FROM order_items WHERE order_id IN (
SELECT id FROM orders WHERE user_id = 1
);
DELETE FROM orders WHERE user_id = 1;
DELETE FROM users WHERE id = 1;
COMMIT;
-- 使用 CTE 删除
WITH deleted_orders AS (
DELETE FROM orders
WHERE user_id = 1
RETURNING id
)
DELETE FROM order_items
WHERE order_id IN (SELECT id FROM deleted_orders);
软删除 #
实现软删除 #
sql
-- 添加删除标记列
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP;
ALTER TABLE users ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE;
-- 软删除(不实际删除,只标记)
UPDATE users
SET
is_deleted = TRUE,
deleted_at = CURRENT_TIMESTAMP
WHERE id = 1;
-- 查询时过滤已删除数据
SELECT * FROM users WHERE is_deleted = FALSE;
-- 创建视图简化查询
CREATE VIEW active_users AS
SELECT id, name, email FROM users WHERE is_deleted = FALSE;
-- 恢复软删除的数据
UPDATE users
SET
is_deleted = FALSE,
deleted_at = NULL
WHERE id = 1;
-- 永久删除软删除的数据
DELETE FROM users
WHERE is_deleted = TRUE
AND deleted_at < CURRENT_DATE - INTERVAL '30 days';
使用触发器自动过滤 #
sql
-- 创建触发器函数
CREATE OR REPLACE FUNCTION soft_delete_filter()
RETURNS TRIGGER AS $$
BEGIN
-- 插入时设置默认值
IF TG_OP = 'INSERT' THEN
NEW.is_deleted := COALESCE(NEW.is_deleted, FALSE);
RETURN NEW;
END IF;
-- 更新时检查
IF TG_OP = 'UPDATE' THEN
IF OLD.is_deleted = TRUE AND NEW.is_deleted = FALSE THEN
NEW.deleted_at := NULL;
END IF;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- 创建触发器
CREATE TRIGGER trg_users_soft_delete
BEFORE INSERT OR UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION soft_delete_filter();
性能优化 #
分批删除 #
sql
-- 大批量删除时分批进行
-- 方法1:使用 LIMIT
DELETE FROM logs
WHERE created_at < '2025-01-01'
AND id IN (
SELECT id FROM logs
WHERE created_at < '2025-01-01'
LIMIT 10000
);
-- 方法2:使用 ID 范围
DELETE FROM logs
WHERE id BETWEEN 1 AND 100000;
-- 方法3:循环删除
DO $$
DECLARE
deleted_count INTEGER;
BEGIN
LOOP
DELETE FROM logs
WHERE created_at < '2025-01-01'
LIMIT 10000;
GET DIAGNOSTICS deleted_count = ROW_COUNT;
EXIT WHEN deleted_count = 0;
COMMIT;
END LOOP;
END $$;
使用索引 #
sql
-- 确保删除条件使用索引
CREATE INDEX idx_logs_created_at ON logs(created_at);
-- 这个删除会使用索引
DELETE FROM logs WHERE created_at < '2025-01-01';
-- 查看执行计划
EXPLAIN DELETE FROM logs WHERE created_at < '2025-01-01';
删除后维护 #
sql
-- 大量删除后执行 VACUUM
DELETE FROM logs WHERE created_at < '2025-01-01';
VACUUM ANALYZE logs;
-- 使用 VACUUM FULL 回收空间(会锁表)
VACUUM FULL logs;
-- 使用 pg_repack 在线回收空间(需要扩展)
-- pg_repack --table=logs database_name
安全删除 #
使用事务 #
sql
-- 使用事务确保安全
BEGIN;
-- 先查询要删除的数据
SELECT * FROM users WHERE status = 'inactive';
-- 确认无误后删除
DELETE FROM users WHERE status = 'inactive';
-- 确认结果
SELECT * FROM users WHERE status = 'inactive';
-- 确认无误后提交
COMMIT;
-- 如果有问题,回滚
-- ROLLBACK;
删除前备份 #
sql
-- 删除前创建备份
CREATE TABLE users_backup AS
SELECT * FROM users WHERE status = 'inactive';
-- 确认备份
SELECT COUNT(*) FROM users_backup;
-- 执行删除
DELETE FROM users WHERE status = 'inactive';
-- 如果需要恢复
INSERT INTO users SELECT * FROM users_backup;
常见错误 #
外键约束错误 #
sql
-- ERROR: update or delete on table "users" violates foreign key constraint
DELETE FROM users WHERE id = 1;
-- 解决方案:
-- 1. 先删除关联数据
DELETE FROM orders WHERE user_id = 1;
DELETE FROM users WHERE id = 1;
-- 2. 使用 CASCADE
-- ALTER TABLE orders DROP CONSTRAINT orders_user_id_fkey;
-- ALTER TABLE orders ADD CONSTRAINT orders_user_id_fkey
-- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
-- 3. 使用 TRUNCATE CASCADE
TRUNCATE users CASCADE;
权限错误 #
sql
-- ERROR: permission denied for table users
DELETE FROM users WHERE id = 1;
-- 解决方案:授予权限
GRANT DELETE ON users TO myuser;
学习路径 #
text
基础阶段
├── 数据库操作
├── 表操作
├── 数据插入
├── 数据更新
├── 数据删除(本文)
└── 基础查询
下一步 #
掌握了数据删除后,接下来学习 基础查询,了解如何查询数据!
最后更新:2026-03-29