[postgresql] 캐스케이드 삭제

계단식 삭제를 수행하려는 Postgresql 데이터베이스가 있습니다. 그러나 테이블은 ON DELETE CASCADE 규칙으로 설정되지 않습니다. 삭제를 수행하고 Postgresql에 단 한 번만 계단식으로 연결하도록 할 수있는 방법이 있습니까? 이에 상응하는 것

DELETE FROM some_table CASCADE;

이 오래된 질문에 대한 답변은 그러한 해결책이 존재하지 않는 것처럼 보이지만 확실하게하기 위해이 질문을 명시 적으로 요구할 것이라고 생각했습니다.



답변

아니요. 캐스케이드하려는 테이블에 대해 delete 문을 작성하기 만하면됩니다.

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충분하지 않습니다.

사용과 CARE는 -이됩니다 모든 테이블의 모든 행 드롭 에 외래 키 제약 조건이 some_table등 해당 테이블에 제약이있는 모든 테이블을

포스트 그레스 지원 CASCADETRUNCATE 명령 :

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

물론 정신 건강을 위해 그런 것들을 절차로 요약해야합니다.


답변

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'(또는 이에 상응하는)는 없습니다. 계단식이 아닌 외래 키로 보호되는 자식 레코드와 참조 조상을 삭제하려면 다음과 같은 옵션을 사용하십시오.

  • 자식 테이블부터 시작하여 한 번에 한 번의 쿼리로 모든 삭제를 명시 적으로 수행하십시오 (순환 참조가있는 경우에는 그렇지 않습니다). 또는
  • 단일 (잠재적으로 대규모) 쿼리에서 명시 적으로 모든 삭제를 수행하십시오. 또는
  • 계단식이 아닌 외래 키 제약 조건이 ‘ON DELETE NO ACTION DEFERRABLE’로 생성되었다고 가정하면 단일 트랜잭션에서 모든 삭제를 명시 적으로 수행하십시오. 또는
  • 그래프에서 ‘조치 없음’및 ‘제한’외래 키 제약 조건을 일시적으로 삭제하고 CASCADE로 다시 작성하고 문제가되는 조상을 삭제하고 외래 키 제약 조건을 다시 삭제 한 다음 최종적으로 원래대로 다시 작성합니다. 귀하의 데이터); 또는
  • 아마도 똑같이 재미있을 것입니다.

외래 키 제약 조건을 피하는 것이 편리하지 않다는 것이 목적입니다. 하지만 특정 상황에서 왜 그렇게하고 싶은지 이해합니다. 그것이 당신이 어떤 빈도로 할 일이고 DBA의 지혜를 어디에서나 기꺼이 털어 내고 싶다면 절차로 자동화하고 싶을 것입니다.

나는 몇 달 전에 “CASCADE DELETE just once”질문에 대한 답을 찾기 위해 이곳에 왔습니다. 나는 Joe Love의 영리한 솔루션 (및 Thomas CG de Vilhena의 변형)에서 약간의 마일리지를 얻었지만 결국에는 유스 케이스에 다른 요구 사항을 적용 해야하는 특정 요구 사항 (테이블 내 순환 참조 처리)이있었습니다. 이 접근 방식은 궁극적으로 재귀 적 으로 삭제되었습니다 (PG 10.10).

나는 프로덕션에서 recursively_delete를 한동안 사용 해 왔으며 마침내 아이디어를 찾고있는 다른 사람들이 사용할 수있을만큼 자신감을 느꼈습니다. Joe Love의 솔루션과 마찬가지로 데이터베이스의 모든 외래 키 제약 조건이 일시적으로 CASCADE로 설정된 것처럼 전체 데이터 그래프를 삭제할 수 있지만 몇 가지 추가 기능을 제공합니다.

  • 삭제 대상의 ASCII 미리보기와 해당 종속 항목의 그래프를 제공합니다.
  • 재귀 CTE를 사용하여 단일 쿼리에서 삭제를 수행합니다.
  • 테이블 내 및 테이블 간 순환 종속성을 처리합니다.
  • 복합 키를 처리합니다.
  • ‘set default’및 ‘set null’제약 조건을 건너 뜁니다.

답변

이를 자동화하는 데 사용할 수 있으며로 외래 키 제약 조건을 정의 할 수 ON DELETE CASCADE있습니다. 외래 키 제약 설명서를
인용하십시오 .

CASCADE 참조 된 행이 삭제 될 때이를 참조하는 행도 자동으로 삭제되도록 지정합니다.