문제

몇 가지 계단식 삭제를하고 싶은 PostgreSQL 데이터베이스가 있습니다. 그러나 ON DELETE 캐스케이드 규칙으로 테이블이 설정되지 않습니다. 삭제를 수행하고 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 충분하지 않습니다.

조심스럽게 사용하십시오 - 이건 윌 모든 테이블의 모든 행을 떨어 뜨립니다 외국의 주요 제약 조건이 있습니다 some_table 그리고 해당 테이블에 제약이있는 모든 테이블 등

Postgres는 지원합니다 CASCADE ~와 함께 절단 명령:

TRUNCATE some_table CASCADE;

손으로 이것은 다른 동시 거래에서 완전히 격리되어 있지 않지만 다른 경고는 있지만, 이것은 거래입니다 (즉, 롤백 될 수 있음). 자세한 내용은 문서를 읽으십시오.

기본 키를 기반으로 행을 삭제하기 위해 (재귀) 함수를 썼습니다. 나는 "On Delete 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 Logick은 괜찮지 만 빅 데이터 세트는 효율성이 좋지 않을 수 있습니다.

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;

열에 인덱스가 있고 데이터 세트가 레코드가 거의없는 경우 더 빠릅니다.

이것을 자동화하는 데 사용할 수 있습니다. 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;

캐스케이드 옵션으로 삭제 된 외래 키가 정의 된 테이블에만 적용됩니다. 삭제를 수행하고 외국 키 제약 조건을 위반하기 때문에 할 수 없다고 말하면 캐스케이드로 인해 문제가 발생합니다.

이런 방식으로 관련 행을 삭제하려면 먼저 외래 키를 정의해야합니다. 또한 거래를 시작하도록 명시 적으로 지시하지 않으면 기본값을 변경하지 않으면 자동 커밋을 수행하여 정리하는 데 시간이 많이 걸릴 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top