PostgreSQL DELETE 语句

PostgreSQL DELETE 语句完全指南

DELETE 是移除数据的核心命令,支持 条件删除、级联删除、返回数据、FROM 子句、性能优化、安全防护 等高级特性。
本文涵盖全部场景,帮助你安全、高效地删除数据


一、DELETE 基本语法

DELETE FROM table_name
[WHERE condition]
[RETURNING * | column1, column2, ...];

没有 WHERE = 删除全表!


二、核心示例表

CREATE TABLE employees (
    id BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT,
    department TEXT,
    salary NUMERIC(10,2),
    is_active BOOLEAN DEFAULT true,
    tags TEXT[] DEFAULT '{}',
    config JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT now(),
    deleted_at TIMESTAMPTZ
);

-- 插入测试数据
INSERT INTO employees (name, email, department, salary, tags, config)
VALUES 
('Alice', 'alice@company.com', 'IT', 75000, '{"dev","lead"}', '{"level": 5}'),
('Bob',   'bob@company.com',   'HR', 52000, '{"hr"}',         '{"level": 3}'),
('Carol', 'carol@company.com', 'IT', 85000, '{"dev","ai"}',   '{"level": 6}'),
('Dave',  'dave@company.com',  'IT', 92000, '{"dev","lead"}', '{"level": 7}'),
('Eve',   'eve@company.com',   'HR', 48000, '{"hr"}',         '{"level": 2}');

三、基础 DELETE 用法

场景SQL
按主键删除DELETE FROM employees WHERE id = 1;
按条件删除DELETE FROM employees WHERE department = 'HR';
多条件DELETE FROM employees WHERE salary < 50000 AND is_active = false;
返回删除数据DELETE FROM employees WHERE name = 'Bob' RETURNING *;
-- 删除并返回
DELETE FROM employees 
WHERE department = 'HR' AND salary < 50000
RETURNING id, name, salary;

输出

 id | name | salary
----+------+--------
  5 | Eve  | 48000

四、关键特性:RETURNING

-- 获取被删除的记录(用于日志、审计)
DELETE FROM employees 
WHERE created_at < '2024-01-01'
RETURNING id, name, email, deleted_at;

用途:审计、软删除前备份、触发后续逻辑


五、条件删除(WHERE 子句)

1. 基础过滤

DELETE FROM employees WHERE is_active = false;

2. 复杂逻辑

DELETE FROM employees 
WHERE (department = 'IT' AND tags @> '{"lead"}')
   OR (salary < 40000 AND created_at < '2023-01-01');

3. 子查询删除

-- 删除薪资低于部门平均的员工
DELETE FROM employees e1
USING (
    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department
) e2
WHERE e1.department = e2.department
  AND e1.salary < e2.avg_salary;

六、关联删除:USING 子句(类似 JOIN)

-- 删除有订单的员工(假设有 orders 表)
DELETE FROM employees e
USING orders o
WHERE e.id = o.employee_id
  AND o.status = 'cancelled';

USING = FROM + JOIN,专为 DELETE 设计


七、级联删除(CASCADE)

1. 外键设置 ON DELETE CASCADE

CREATE TABLE orders (
    id BIGSERIAL PRIMARY KEY,
    employee_id BIGINT REFERENCES employees(id) ON DELETE CASCADE
);

-- 删除员工 → 自动删除其订单
DELETE FROM employees WHERE id = 1;

2. 手动级联(无外键)

-- 先删子表,再删主表
DELETE FROM orders WHERE employee_id = 1;
DELETE FROM employees WHERE id = 1;

八、软删除(推荐生产实践)

-- 1. 添加 deleted_at 字段
ALTER TABLE employees ADD COLUMN deleted_at TIMESTAMPTZ;

-- 2. 软删除
UPDATE employees 
SET deleted_at = now(), is_active = false 
WHERE id = 1;

-- 3. 查询时过滤
SELECT * FROM employees WHERE deleted_at IS NULL;

优点:可恢复、有审计
缺点:数据膨胀,需索引

-- 为软删除建索引
CREATE INDEX idx_emp_deleted ON employees(deleted_at) WHERE deleted_at IS NULL;

九、安全删除:防止误删

1. 永远加 WHERE

-- 危险!删全表
DELETE FROM employees;

-- 安全写法
DELETE FROM employees WHERE id = 999;

2. 先 SELECT 验证

-- 先看要删的行
SELECT * FROM employees WHERE department = 'HR' AND salary < 50000;

-- 再删除
DELETE FROM employees WHERE department = 'HR' AND salary < 50000;

3. 事务回滚

BEGIN;
DELETE FROM employees WHERE name = 'Alice';
-- 检查
SELECT * FROM employees;
-- 不满意?回滚
ROLLBACK;
-- 满意?提交
COMMIT;

十、性能优化

技巧说明
加索引WHERE 列建索引
批量删除分批执行,避免锁表
避免函数列WHERE UPPER(name) = 'ALICE' → 慢
分区表删除分区 = 秒删亿级
-- 分批删除(每批 1000 条)
DO $$
BEGIN
    FOR i IN 1..100 LOOP
        DELETE FROM employees 
        WHERE id IN (
            SELECT id FROM employees 
            WHERE created_at < '2020-01-01' 
            LIMIT 1000
        );
        EXIT WHEN NOT FOUND;
    END LOOP;
END $$;

十一、触发器:删除前/后操作

-- 记录删除日志
CREATE TABLE employee_delete_log (
    emp_id BIGINT,
    name TEXT,
    deleted_at TIMESTAMPTZ DEFAULT now()
);

CREATE OR REPLACE FUNCTION log_employee_delete()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO employee_delete_log (emp_id, name)
    VALUES (OLD.id, OLD.name);
    RETURN OLD;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_emp_delete
    BEFORE DELETE ON employees
    FOR EACH ROW
    EXECUTE FUNCTION log_employee_delete();

十二、常见错误与解决

错误原因解决
DELETE 0 rowsWHERE 不匹配检查条件
cannot delete from table because it is referenced外键约束CASCADE 或先删子表
permission denied无权限GRANT DELETE ON employees TO user;
deadlock detected并发删除重试或分批

十三、一键安全删除脚本

-- 安全删除:备份 + 软删除 + 日志
WITH deleted AS (
    DELETE FROM employees
    WHERE department = 'HR' AND salary < 50000
    RETURNING id, name, email, now() AS deleted_at
)
INSERT INTO employee_archive (id, name, email, deleted_at, data)
SELECT id, name, email, deleted_at, row_to_json(employees.*)
FROM deleted;

十四、最佳实践 Checklist

项目建议
永远加 WHERE防止全表删除
用 RETURNING获取删除数据
先 SELECT 验证确认影响行
用事务可回滚
软删除优先生产环境
建索引WHERE 列
分批删除大表

十五、快速上手:5 分钟掌握 DELETE

-- 1. 简单删除
DELETE FROM employees WHERE name = 'Bob';

-- 2. 条件 + 返回
DELETE FROM employees 
WHERE salary < 50000 
RETURNING id, name, salary;

-- 3. USING 联合删除
DELETE FROM employees e
USING (VALUES ('HR', 50000)) AS v(dept, max_sal)
WHERE e.department = v.dept AND e.salary < v.max_sal;

-- 4. 软删除
UPDATE employees 
SET deleted_at = now(), is_active = false 
WHERE department = 'IT' AND created_at < '2023-01-01';

现在就动手
employees 表中:

  1. 删除 salary < 60000is_active = false 的员工,RETURNING *
  2. USING 删除 department = 'HR' 且有 tags @> '{"hr"}' 的员工
  3. 实现软删除:添加 deleted_at 字段,更新代替删除
  4. 写一个防误删事务(BEGIN; ... ROLLBACK;

需要我生成:

  • 1000 条测试数据 + 批量 DELETE 脚本?回复 测试数据
  • 自动归档触发器?回复 归档触发器
  • Python 安全 DELETE?回复 Python DELETE
  • 分区表秒删亿级数据?回复 分区删除

随时告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注