回答:
いいえ。1回だけ行うには、カスケードするテーブルの削除ステートメントを記述するだけです。
DELETE FROM some_child_table WHERE some_fk_field IN (SELECT some_id FROM some_Table);
DELETE FROM some_table;
あなたが本当にしたい場合 DELETE FROM some_table CASCADE;
を意味し、「テーブルからすべての行を削除するsome_table
あなたが使用することができ、」TRUNCATE
の代わりにDELETE
してCASCADE
、常にサポートされています。ただし、where
節で選択的削除を使用する場合TRUNCATE
は、十分ではありません。
USE WITH CARE -これはしますすべてのテーブルのすべての行ドロップで外部キー制約持っているsome_table
など、およびそれらのテーブル上の制約を持っているすべてのテーブルを
PostgresはTRUNCATEコマンドでサポートCASCADE
しています:
TRUNCATE some_table CASCADE;
これは、他の同時トランザクションから完全に分離されているわけではありませんが、便利です(つまり、ロールバックできます)。詳細については、ドキュメントをご覧ください。
主キーに基づいて行を削除する(再帰的な)関数を作成しました。「カスケードの削除時」として制約を作成したくなかったので、これを書きました。(DBAとして)複雑なデータセットを削除できるようにしたいが、プログラマーがすべての影響を熟考せずにカスケード削除できるようにしたくありませんでした。私はまだこの関数をテストしていますので、バグがあるかもしれません-しかし、DBに複数列のプライマリ(したがって外部)キーがある場合は、試さないでください。また、キーはすべて文字列形式で表現できる必要がありますが、その制限がない方法で書き込むこともできます。とにかく、この関数を非常に少しずつ使用していますが、データの値が多すぎるため、すべてのカスケード制約を有効にできません。基本的に、この関数はスキーマ、テーブル名、およびプライマリ値(文字列形式)で渡されます。そして、そのテーブルで外部キーを見つけることから始め、データが存在しないことを確認します。存在する場合は、見つかったデータに対して再帰的に呼び出します。無限ループを防ぐために、すでに削除のマークが付けられているデータの配列を使用します。試してみて、動作を教えてください。注:少し遅いです。私はそれを次のように呼びます:
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;
IN
代わりに演算子を使用するクエリを生成する場合=
(セットロジックを使用するための手順)ではなく、サブセレクトを使用してクエリを生成すると、はるかに高速になります。
私が正しく理解している場合は、外部キー制約を削除し、新しい制約を追加(カスケード)し、必要なことを実行して、制限する外部キー制約を再作成することで、必要なことができるはずです。
例えば:
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
もちろん、メンタルヘルスのために、そのようなものを手順に抽象化する必要があります。
Palehorseの回答にはコメントできないので、自分の回答を追加しました。Palehorseのロジックは問題ありませんが、ビッグデータセットでは効率が悪くなる可能性があります。
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」(または同等のもの)はありません。カスケード以外の外部キーで保護された子レコードとそれらの参照先の祖先を削除するには、次のオプションがあります。
外部キーの制約を回避することが便利にならないのは意図的なものだと思います。しかし、特定の状況でなぜそれを実行したいのかは理解しています。それがある程度の頻度で実行することであり、DBAの知恵をどこでも活用したい場合は、手順を使用して自動化することができます。
私は数か月前にここに来て、「CASCADE DELETE only once」の質問(元々は10年以上前に尋ねられました!)に対する回答を探しました。Joe Loveの巧妙なソリューション(およびThomas CG de Vilhenaのバリアント)からいくらかマイレージを得ましたが、結局、私のユースケースには特定の要件(テーブル内循環参照の処理など)があり、別のアプローチをとらざるを得ませんでした。そのアプローチは最終的にrecursively_delete(PG 10.10)になりました。
私はしばらくの間、本番環境でrecursively_deleteを使用してきましたが、ようやく、アイデアを求めてここにくるくる回る他の人がそれを利用できるようになるのに十分(自信を持って)自信がついてきました。Joe Loveのソリューションと同様に、データベースのすべての外部キー制約が一時的にCASCADEに設定されているかのように、データのグラフ全体を削除できますが、いくつかの追加機能を提供します。
を使用してこれを自動化できますON DELETE CASCADE
。外部キー制約をで定義できます。
私は引用します外部キー制約のマニュアルます:
CASCADE
参照された行が削除されると、それを参照している行も自動的に削除されることを指定します。
私は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;
in
演算子とサブクエリを使用すると、10倍程度の高速化に気づきました。
カスケードオプションを使用した削除は、外部キーが定義されているテーブルにのみ適用されます。削除すると、外部キー制約に違反するために削除できないと表示された場合、カスケードにより問題の行が削除されます。
この方法で関連する行を削除する場合は、最初に外部キーを定義する必要があります。また、トランザクションを開始するように明示的に指示したり、デフォルトを変更したりしない限り、自動コミットが実行されるため、クリーンアップに非常に時間がかかる可能性があることに注意してください。