240 votes

EFFACER en CASCADE juste une fois

J'ai une base de données Postgresql sur lequel je veux faire un peu de suppressions en cascade. Cependant, les tableaux ne sont pas mis en place avec la clause on DELETE CASCADE de règle. Est il possible que je peux supprimer et dire Postgresql cascade juste cette fois? Quelque chose d'équivalent à

DELETE FROM some_table CASCADE;

Les réponses à cette ancienne question de le faire paraître comme pas une telle solution existe, mais j'ai pensé que je voudrais poser cette question explicitement juste pour être sûr.

200voto

palehorse Points 8268

Pas de. Le faire juste une fois, vous pouvez tout simplement écrire l'instruction delete pour la table que vous souhaitez cascade.

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

40voto

DanC Points 591

Sur Postgres, vous pouvez utiliser la commande TRUNCATE, en supposant que vous ne souhaitez pas spécifier une clause where:

TRUNCATE some_table CASCADE;

Idéalement c'est transactionnelle (c'est à dire peut être annulée), même si elle n'est pas complètement isolé des autres transactions simultanées, et a plusieurs autres mises en garde. Lisez la documentation pour plus de détails.

32voto

Joe Love Points 619

J'ai écrit un (récursif) la fonction pour supprimer une ligne basé sur sa clé primaire. J'ai écrit cela parce que je n'ai pas envie de créer mes contraintes "on delete cascade". Je voulais être en mesure de supprimer des ensembles complexes de données (DBA) mais ne permet pas à mon programmeurs pour être en mesure de supprimer en cascade sans réfléchir à toutes les conséquences. Je suis toujours tester cette fonction, donc il peut y avoir des bugs, mais s'il vous plaît ne pas essayer si votre DB, le multi colonne primaire (et donc étrangère) de la télécommande. Aussi, les clés de tous afin de pouvoir être représenté dans la forme d'une chaîne, mais il pourrait être écrit d'une manière qui n'a pas cette restriction. J'utilise cette fonction avec beaucoup de PARCIMONIE, de toute façon, j'en valeur mes données trop pour activer la cascade de contraintes sur tout. Fondamentalement, cette fonction est passée dans le schéma, le nom de la table, et la valeur primaire (dans la forme d'une chaîne), et il va commencer par trouver les clés étrangères de la table et permet de s'assurer de données n'existe pas, s'il n', il appelle de manière récursive en soi sur les données trouvées. Il utilise un tableau de données déjà marqué pour suppression pour éviter les boucles infinies. Merci de le tester et laissez-moi savoir comment cela fonctionne pour vous. Remarque: Il est un peu lent. Je l'appelle comme ça: 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 +dallas.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;

18voto

Ryszard Szopa Points 2116

Si je comprends bien, vous devriez être en mesure de faire ce que vous voulez en supprimant la contrainte de clé étrangère, l'ajout d'un nouveau (qui sera en cascade), fais de ton stuff, et de recréer la restriction de la contrainte de clé étrangère.

Par exemple:

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

Bien sûr, vous devez de choses abstraites comme ça dans une procédure, pour le bien de votre santé mentale.

2voto

Grant Johnson Points 968

Le supprimer avec l'option cascade uniquement appliqués à des tables avec des clés étrangères définies. Si vous ne les supprimer, et il dit que vous ne pouvez parce que ce serait en violation de la contrainte de clé étrangère, la cascade va l'amener à supprimer la délinquance lignes.

Si vous souhaitez supprimer les lignes associées de cette façon, vous aurez besoin de définir les clés étrangères en premier. Aussi, n'oubliez pas que si vous l'avez explicitement demander d'entamer une transaction, ou si vous modifiez les paramètres par défaut, il fera une auto-commit, ce qui pourrait être très coûteux en temps de nettoyer.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X