いくつかのカスケード削除を実行したい Postgresql データベースがあります。ただし、テーブルは ON DELETE CASCADE ルールで設定されていません。削除を実行して Postgresql に一度だけカスケードするように指示する方法はありますか? に相当するもの
DELETE FROM some_table CASCADE;
この古い質問への回答は、そのような解決策が存在しないように思われますが、念のため、この質問を明示的に行うことにしました。
いくつかのカスケード削除を実行したい Postgresql データベースがあります。ただし、テーブルは ON DELETE CASCADE ルールで設定されていません。削除を実行して Postgresql に一度だけカスケードするように指示する方法はありますか? に相当するもの
DELETE FROM some_table CASCADE;
この古い質問への回答は、そのような解決策が存在しないように思われますが、念のため、この質問を明示的に行うことにしました。
いいえ。一度だけ実行するには、カスケードするテーブルの削除ステートメントを記述するだけです。
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;
私が正しく理解していれば、外部キー制約を削除し、新しいものを追加し(カスケードします)、自分のことを行い、制限する外部キー制約を再作成することで、必要なことを実行できるはずです。
例えば:
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
もちろん、メンタルヘルスのために、そのようなものを手順に抽象化する必要があります.
カスケードオプションを使用した削除は、外部キーが定義されているテーブルにのみ適用されます。削除を実行し、外部キー制約に違反するために削除できないと表示された場合、カスケードにより、問題のある行が削除されます。
この方法で関連する行を削除する場合は、最初に外部キーを定義する必要があります。また、トランザクションを開始するように明示的に指示しない限り、またはデフォルトを変更しない限り、自動コミットが実行されるため、クリーンアップに非常に時間がかかる可能性があることに注意してください。