2024-07-07 07:00:03

级联删除一次

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

DELETE FROM some_table CASCADE;

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


当前回答

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

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;

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

其他回答

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

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;

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

是的,正如其他人所说,没有方便的“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语句。

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

我拿了Joe Love的答案,用IN操作符和子选择来重写它,而不是=来让函数更快(根据Hubbitus的建议):

create or replace function delete_cascade(p_schema varchar, p_table varchar, p_keys varchar, p_subquery varchar default null, p_foreign_keys varchar[] default array[]::varchar[])
 returns integer as $$
declare

    rx record;
    rd record;
    v_sql varchar;
    v_subquery varchar;
    v_primary_key varchar;
    v_foreign_key varchar;
    v_rows integer;
    recnum integer;

begin

    recnum := 0;
    select ccu.column_name into v_primary_key
        from
        information_schema.table_constraints  tc
        join information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name and ccu.constraint_schema=tc.constraint_schema
        and tc.constraint_type='PRIMARY KEY'
        and tc.table_name=p_table
        and tc.table_schema=p_schema;

    for rx in (
        select kcu.table_name as foreign_table_name, 
        kcu.column_name as foreign_column_name, 
        kcu.table_schema foreign_table_schema,
        kcu2.column_name as foreign_table_primary_key
        from information_schema.constraint_column_usage ccu
        join information_schema.table_constraints tc on tc.constraint_name=ccu.constraint_name and tc.constraint_catalog=ccu.constraint_catalog and ccu.constraint_schema=ccu.constraint_schema 
        join information_schema.key_column_usage kcu on kcu.constraint_name=ccu.constraint_name and kcu.constraint_catalog=ccu.constraint_catalog and kcu.constraint_schema=ccu.constraint_schema
        join information_schema.table_constraints tc2 on tc2.table_name=kcu.table_name and tc2.table_schema=kcu.table_schema
        join information_schema.key_column_usage kcu2 on kcu2.constraint_name=tc2.constraint_name and kcu2.constraint_catalog=tc2.constraint_catalog and kcu2.constraint_schema=tc2.constraint_schema
        where ccu.table_name=p_table  and ccu.table_schema=p_schema
        and TC.CONSTRAINT_TYPE='FOREIGN KEY'
        and tc2.constraint_type='PRIMARY KEY'
)
    loop
        v_foreign_key := rx.foreign_table_schema||'.'||rx.foreign_table_name||'.'||rx.foreign_column_name;
        v_subquery := 'select "'||rx.foreign_table_primary_key||'" as key from '||rx.foreign_table_schema||'."'||rx.foreign_table_name||'"
             where "'||rx.foreign_column_name||'"in('||coalesce(p_keys, p_subquery)||') for update';
        if p_foreign_keys @> ARRAY[v_foreign_key] then
            --raise notice 'circular recursion detected';
        else
            p_foreign_keys := array_append(p_foreign_keys, v_foreign_key);
            recnum:= recnum + delete_cascade(rx.foreign_table_schema, rx.foreign_table_name, null, v_subquery, p_foreign_keys);
            p_foreign_keys := array_remove(p_foreign_keys, v_foreign_key);
        end if;
    end loop;

    begin
        if (coalesce(p_keys, p_subquery) <> '') then
            v_sql := 'delete from '||p_schema||'."'||p_table||'" where "'||v_primary_key||'"in('||coalesce(p_keys, p_subquery)||')';
            --raise notice '%',v_sql;
            execute v_sql;
            get diagnostics v_rows = row_count;
            recnum := recnum + v_rows;
        end if;
        exception when others then recnum=0;
    end;

    return recnum;

end;
$$
language PLPGSQL;

你可以用ON DELETE CASCADE来定义外键约束。 我引用外键约束的手册:

CASCADE指定当一个被引用的行被删除时, 引用它也应该被自动删除。