Frage

Ich habe eine PostgreSQL-Datenbank, auf das ich löscht ein paar Kaskadierung tun will. Allerdings werden die Tabellen nicht mit der ON DELETE CASCADE-Regel einzurichten. Gibt es eine Weise, die ich durchführen kann ein löschen und sagen Postgresql es kaskadieren nur dieses eine Mal? Etwas äquivalent zu

DELETE FROM some_table CASCADE;

Die Antworten href="https://stackoverflow.com/questions/45611/cascading-deletes-in-postgresql"> diese ältere Frage es wie scheinen keine solche Lösung existiert, aber ich dachte ich, diese Frage stellen würde ausdrücklich nur um sicher zu sein.

War es hilfreich?

Lösung

Nein. Um es einfach zu tun, wenn Sie einfach die Delete-Anweisung für die Tabelle schreiben möchten Sie kaskadieren möchten.

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

Andere Tipps

Ich schrieb eine (rekursiv) Funktion jede Zeile zu löschen, basierend auf seinem Primärschlüssel. Ich schrieb dies, weil ich nicht als „auf DELETE CASCADE“ meine Zwänge schaffen wollte. Ich wollte komplexe Sätze von Daten löschen können (als DBA) aber nicht zulassen, dass meine Programmierer ohne durch all die Auswirkungen zu denken löschen kaskadieren können. Ich teste noch diese Funktion aus, so kann es Fehler drin sein - aber bitte versuchen Sie es nicht, wenn Ihre DB mehrspaltigen Primär (und damit ausländischen) Schlüssel hat. Auch alle die Schlüssel haben, um in String-Form dargestellt werden, aber es könnte in einer Art und Weise geschrieben werden, die nicht diese Einschränkung haben. Ich benutze diese Funktion sehr sparsam wie auch immer, ich schätze zu viel meine Daten, die die Kaskadierung Einschränkungen für alles zu ermöglichen. Grundsätzlich ist diese Funktion im Schema, Tabellenname und Primärwert (in String-Form) übergeben, und es wird von der Suche keine Fremdschlüssel auf die Tabelle beginnen und sicher Daten macht nicht exist-- wenn es funktioniert, es rekursiv ruft itsself auf den gefundenen Daten. Es verwendet eine Reihe von Daten, die bereits zum Löschen markiert, um Endlosschleifen zu verhindern. Bitte testen Sie es aus und lassen Sie mich wissen, wie es für Sie arbeitet. Hinweis: Es ist ein wenig langsam. Ich nenne es wie folgt: 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;

Wenn ich richtig verstehe, sollten Sie in der Lage sein zu tun, was Sie wollen, indem Sie den Fremdschlüssel fallen und fügte hinzu, ein neues (was kaskadieren), tun Sie Ihre Sachen, und die Einschränkung Fremdschlüssel neu zu erstellen.

Zum Beispiel:

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

Natürlich sollten Sie abstrakte Sachen wie, dass in einem Verfahren, zum Wohle Ihrer geistigen Gesundheit.

kann ich nicht kommentieren palehorse Antwort, so kann ich meine eigene Antwort gegeben. Palehorse logick ist ok, aber Effizienz kann mit großen Datensätzen schlecht sein.

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;

Es ist schneller, wenn Sie Indizes für Spalten und Datensatz ist größer dann einige Datensätze.

Sie können mit, dies automatisieren, können Sie die Fremdschlüssel mit ON DELETE CASCADE definieren könnte.
Ich zitiere den dem Handbuch für Fremdschlüssel :

  

CASCADE gibt an, dass, wenn eine referenzierte Zeile gelöscht wird, Zeile (n)   Referenzierung es auch automatisch gelöscht werden sollen.

habe ich Joe Liebe Antwort und schrieb es den IN Operator mit Unter wählt statt = mit der Funktion schneller zu machen (nach Hubbitus Vorschlag):

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;

Das Löschen mit der CASCADE-Option nur für Tabellen mit Fremdschlüssel angewandt definiert. Wenn Sie einen Lösch tun, und es sagt, kann man nicht, weil es die Fremdschlüssel verletzen würde, bewirkt, dass die Kaskade es die problematischen Zeilen löschen.

Wenn Sie zugehörigen Zeilen auf diese Weise löschen möchten, müssen Sie zuerst die Fremdschlüssel definieren. Denken Sie auch daran, dass, wenn Sie explizit anweisen, eine Transaktion zu beginnen, oder Sie die Standardeinstellungen ändern, wird es tun ein Auto begehen, was sehr zeitaufwendig sein könnte, um aufzuräumen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top