2024-07-07 07:00:03

级联删除一次

我有一个Postgresql数据库,我想做一些级联删除。但是,这些表不是用ON DELETE CASCADE规则设置的。有没有办法我可以执行一个删除,并告诉Postgresql级联它只是这一次?等价于

DELETE FROM some_table CASCADE;

这个老问题的答案似乎不存在这样的解决方案,但我想我要明确地问这个问题,只是为了确定。


当前回答

如果我理解正确的话,您应该能够通过删除外键约束、添加一个新的外键约束(它将级联)、做您的事情和重新创建限制外键约束来做您想做的事情。

例如:

testing=# create table a (id integer primary key);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "a_pkey" for table "a"
CREATE TABLE
testing=# create table b (id integer references a);
CREATE TABLE

-- put some data in the table
testing=# insert into a values(1);
INSERT 0 1
testing=# insert into a values(2);
INSERT 0 1
testing=# insert into b values(2);
INSERT 0 1
testing=# insert into b values(1);
INSERT 0 1

-- restricting works
testing=# delete from a where id=1;
ERROR:  update or delete on table "a" violates foreign key constraint "b_id_fkey" on table "b"
DETAIL:  Key (id)=(1) is still referenced from table "b".

-- find the name of the constraint
testing=# \d b;
       Table "public.b"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | 
Foreign-key constraints:
    "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id)

-- drop the constraint
testing=# alter table b drop constraint b_a_id_fkey;
ALTER TABLE

-- create a cascading one
testing=# alter table b add FOREIGN KEY (id) references a(id) on delete cascade; 
ALTER TABLE

testing=# delete from a where id=1;
DELETE 1
testing=# select * from a;
 id 
----
  2
(1 row)

testing=# select * from b;
 id 
----
  2
(1 row)

-- it works, do your stuff.
-- [stuff]

-- recreate the previous state
testing=# \d b;
       Table "public.b"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | 
Foreign-key constraints:
    "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id) ON DELETE CASCADE

testing=# alter table b drop constraint b_id_fkey;
ALTER TABLE
testing=# alter table b add FOREIGN KEY (id) references a(id) on delete restrict; 
ALTER TABLE

当然,为了你的心理健康,你应该把这样的东西抽象成一个程序。

其他回答

当你创建新表时,你可以添加一些约束,如UNIQUE,或NOT NULL,也可以告诉SQL当你试图删除行时,它应该做什么动作,它对另一个表有引用

CREATE TABLE company (
                id SERIAL PRIMARY KEY,
                name VARCHAR(128),
                year DATE);
CREATE TABLE employee (
                id SERIAL PRIMARY KEY,
                first_name VARCHAR(128) NOT NULL,
                last_name VARCHAR(128) NOT NULL,
                company_id INT REFERENCES company(id) ON DELETE CASCADE,
                salary INT,
                UNIQUE (first_name, last_name));

所以在这之后,你可以删除任何你需要的行,例如:

DELETE
FROM company
WHERE id = 2;

不。要做到这一点,只需为要级联的表编写delete语句。

DELETE FROM some_child_table WHERE some_fk_field IN (SELECT some_id FROM some_Table);
DELETE FROM some_table;

带cascade选项的删除只应用于定义了外键的表。如果您执行删除操作,而它告诉您不能删除,因为这会违反外键约束,级联将导致它删除违规行。

如果希望以这种方式删除关联的行,则需要首先定义外键。另外,请记住,除非您显式地指示它开始一个事务,或者更改默认值,否则它将进行自动提交,清理这可能非常耗时。

是的,正如其他人所说,没有方便的“DELETE FROM my_table…”CASCADE'(或等效)。要删除非级联外键保护的子记录及其引用的祖先,您的选项包括:

Perform all the deletions explicitly, one query at a time, starting with child tables (though this won't fly if you've got circular references); or Perform all the deletions explicitly in a single (potentially massive) query; or Assuming your non-cascading foreign key constraints were created as 'ON DELETE NO ACTION DEFERRABLE', perform all the deletions explicitly in a single transaction; or Temporarily drop the 'no action' and 'restrict' foreign key constraints in the graph, recreate them as CASCADE, delete the offending ancestors, drop the foreign key constraints again, and finally recreate them as they were originally (thus temporarily weakening the integrity of your data); or Something probably equally fun.

我想,绕过外键约束是故意不方便的;但我能理解为什么在特定情况下你想这么做。如果这是您经常要做的事情,并且您愿意无视dba的智慧,那么您可能希望通过一个过程将其自动化。

几个月前,我来到这里寻找“CASCADE DELETE only once”问题的答案(最初在十多年前被问到!)。我从Joe Love的聪明的解决方案(以及Thomas C. G. de Vilhena的变体)中获得了一些好处,但最终我的用例有特殊的需求(例如处理表内循环引用),这迫使我采取不同的方法。这种方法最终变成了recursively_delete (PG 10.10)。

我已经在生产环境中使用recursively_delete有一段时间了,现在,我终于有足够的信心(谨慎地)将它提供给其他可能在这里寻找想法的人。与Joe Love的解决方案一样,它允许你删除整个数据图,就像你数据库中的所有外键约束都被暂时设置为CASCADE一样,但它提供了一些额外的功能:

的图形的删除目标的ASCII预览 家属。 使用递归cte在单个查询中执行删除。 处理表内和表间的循环依赖关系。 处理复合键。 跳过'set default'和'set null'约束。

我不能评论帕勒霍斯的答案,所以我加上了我自己的答案。 帕勒霍斯的逻辑是可以的,但大数据集的效率可能很差。

DELETE FROM some_child_table sct 
 WHERE exists (SELECT FROM some_Table st 
                WHERE sct.some_fk_fiel=st.some_id);

DELETE FROM some_table;

如果列上有索引,数据集比少数记录大,那么速度会更快。