Pergunta

Eu tenho um banco de dados PostgreSQL em que eu quero fazer algumas exclusões em cascata. No entanto, as tabelas não são criadas com a regra CASCADE ON DELETE. Existe alguma maneira eu posso executar uma exclusão e dizer Postgresql em cascata que só desta vez? Algo equivalente a

DELETE FROM some_table CASCADE;

As respostas a esta questão mais velho fazer parecer que não existe tal solução, mas eu percebi que eu fazer esta pergunta de forma explícita apenas para ter certeza.

Foi útil?

Solução

No. Para fazê-lo apenas uma vez você poderia simplesmente escrever a declaração de exclusão para a tabela que você deseja cascata.

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

Outras dicas

Se você realmente quiser DELETE FROM some_table CASCADE; que significa " remover todas as linhas da tabela some_table ", você pode usar TRUNCATE vez de DELETE e CASCADE é sempre suportado. No entanto, se você quiser usar seletiva de exclusão com uma cláusula where, TRUNCATE não é bom o suficiente.

Use com cuidado - Isto irá soltar todas as linhas de todas as tabelas que têm uma restrição de chave estrangeira em some_table e todas as tabelas que têm restrições sobre essas tabelas, etc.

Postgres suporta CASCADE com TRUNCATE comando :

TRUNCATE some_table CASCADE;

Com folga esta é transacional (ou seja, pode ser revertida), embora não esteja completamente isolada de outras transações simultâneas, e tem várias outras ressalvas. Leia a documentação para mais detalhes.

Eu escrevi uma função (recursivo) para excluir qualquer linha com base em sua chave primária. Eu escrevi isso porque eu não quero criar minhas limitações como "em cascata delete". Eu queria ser capaz de excluir conjuntos complexos de dados (como um DBA), mas não permitir que os meus programadores para ser capaz de exclusão em cascata sem pensar através de todas as repercussões. Eu ainda estou testando esta função, portanto, pode haver erros no-lo - mas por favor, não tente fazer isso se o seu DB tem teclas múltiplas colunas principais (e, portanto, estrangeiros). Além disso, as teclas de tudo tem que ser capaz de ser representado em forma de corda, mas poderia ser escrito de uma forma que não tem essa restrição. Eu uso essa função de forma muito restritiva de qualquer maneira, eu valorizo ??meus dados demais para permitir que as restrições em cascata sobre tudo. Basicamente esta função é passada no esquema, nome da tabela, e valor primário (em forma de string), e vai começar por encontrar quaisquer chaves estrangeiras nessa tabela e torna os dados certeza não exist-- se isso acontecer, ele chama recursivamente itsself nos dados encontrados. Ele utiliza uma matriz de dados já marcados para eliminação para evitar ciclos infinitos. Por favor, testá-lo e deixe-me saber como ele funciona para você. Nota: É um pouco lento. Eu chamo-lhe assim: select delete_cascade('public','my_table','1');

create or replace function delete_cascade(p_schema varchar, p_table varchar, p_key varchar, p_recursion varchar[] default null)
 returns integer as $$
declare
    rx record;
    rd record;
    v_sql varchar;
    v_recursion_key varchar;
    recnum integer;
    v_primary_key varchar;
    v_rows 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_sql := 'select '||rx.foreign_table_primary_key||' as key from '||rx.foreign_table_schema||'.'||rx.foreign_table_name||'
            where '||rx.foreign_column_name||'='||quote_literal(p_key)||' for update';
        --raise notice '%',v_sql;
        --found a foreign key, now find the primary keys for any data that exists in any of those tables.
        for rd in execute v_sql
        loop
            v_recursion_key=rx.foreign_table_schema||'.'||rx.foreign_table_name||'.'||rx.foreign_column_name||'='||rd.key;
            if (v_recursion_key = any (p_recursion)) then
                --raise notice 'Avoiding infinite loop';
            else
                --raise notice 'Recursing to %,%',rx.foreign_table_name, rd.key;
                recnum:= recnum +delete_cascade(rx.foreign_table_schema::varchar, rx.foreign_table_name::varchar, rd.key::varchar, p_recursion||v_recursion_key);
            end if;
        end loop;
    end loop;
    begin
    --actually delete original record.
    v_sql := 'delete from '||p_schema||'.'||p_table||' where '||v_primary_key||'='||quote_literal(p_key);
    execute v_sql;
    get diagnostics v_rows= row_count;
    --raise notice 'Deleting %.% %=%',p_schema,p_table,v_primary_key,p_key;
    recnum:= recnum +v_rows;
    exception when others then recnum=0;
    end;

    return recnum;
end;
$$
language PLPGSQL;

Se bem entendi, você deve ser capaz de fazer o que quiser, largando a restrição de chave estrangeira, acrescentando um novo (que serão em cascata), fazendo suas coisas, e recriar a restrição restrição de chave estrangeira.

Por exemplo:

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

É claro, você deve coisas abstrato como que em um procedimento, para o bem da sua saúde mental.

Não posso comentar resposta palehorse então eu adicionei a minha própria resposta. logick Palehorse é ok, mas a eficiência pode ser ruim com grandes conjuntos de dados.

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;

É mais rápido se você tiver índices em colunas e conjunto de dados é maior, em seguida, alguns registros.

Você pode usar para automatizar isso, você pode definir a restrição de chave estrangeira com ON DELETE CASCADE.
Cito o o manual de chaves estrangeiras :

especifica CASCADE que quando uma linha referenciada é eliminado, linha (s) fazendo referência a ele deve ser excluído automaticamente.

Eu levei a resposta de Joe Amor e reescreveu-lo usando o operador IN com subseleções em vez de = para fazer a função mais rápido (de acordo com a sugestão de 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;

A exclusão com a opção de cascata aplicada somente às tabelas com chaves estrangeiras definidas. Se você fizer uma exclusão, e ele diz que você não pode porque violaria a restrição de chave estrangeira, a cascata fará com que ele excluir as linhas ofensivas.

Se você quiser excluir linhas associadas desta forma, você precisará definir as chaves estrangeiras em primeiro lugar. Além disso, lembre-se que, a menos que explicitamente instruí-lo a iniciar uma transação, ou você alterar os padrões, ele vai fazer um auto-commit, o que pode ser muito demorado para limpar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top