2017-09-14 18 views
0

데이터베이스에 두 개의 테이블이 있습니다. 하나는 PS_POST이고 다른 하나는 PS_STAR입니다. 여기캐스케이드 및 트리거를 함께 사용하면 오라클 데이터베이스에서 삭제 오류가 발생합니다.

이 DDL입니다 :

-------------------------------------------------------- 
-- DDL for Table PS_POST 
-------------------------------------------------------- 

    CREATE TABLE "C##STY"."PS_POST" 
    ( "POST_ID" NUMBER GENERATED ALWAYS AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE , 
    "USER_ID" NUMBER, 
    "GROUP_ID" NUMBER DEFAULT 0, 
    "TIME" DATE, 
    "TITLE" VARCHAR2(200 BYTE) DEFAULT 'Default_title', 
    "STAR_NUMBER" NUMBER DEFAULT 0, 
    "CONTENT" VARCHAR2(4000 BYTE) DEFAULT 'Default_content' 
    ) ; 
-------------------------------------------------------- 
-- DDL for Index POST_PK 
-------------------------------------------------------- 

    CREATE UNIQUE INDEX "C##STY"."POST_PK" ON "C##STY"."PS_POST" ("POST_ID") ; 
-------------------------------------------------------- 
-- Constraints for Table PS_POST 
-------------------------------------------------------- 

    ALTER TABLE "C##STY"."PS_POST" MODIFY ("USER_ID" NOT NULL ENABLE); 
    ALTER TABLE "C##STY"."PS_POST" ADD CONSTRAINT "POST_PK" PRIMARY KEY ("POST_ID") 
    ENABLE; 
    ALTER TABLE "C##STY"."PS_POST" MODIFY ("POST_ID" NOT NULL ENABLE); 



-------------------------------------------------------- 
-- DDL for Table PS_STAR 
-------------------------------------------------------- 

    CREATE TABLE "C##STY"."PS_STAR" 
    ( "USER_ID" NUMBER, 
    "POST_ID" NUMBER, 
    "TIME" DATE 
    ) ; 
-------------------------------------------------------- 
-- DDL for Index STAR_PK 
-------------------------------------------------------- 

    CREATE UNIQUE INDEX "C##STY"."STAR_PK" ON "C##STY"."PS_STAR" ("USER_ID", "POST_ID") 
    ; 
-------------------------------------------------------- 
-- DDL for Trigger STAR_TRIGGER 
-------------------------------------------------------- 

    CREATE OR REPLACE EDITIONABLE TRIGGER "C##STY"."STAR_TRIGGER" after insert on PS_STAR 
referencing new as new old as old 
for each row 
begin 
update PS_POST 
set 
STAR_NUMBER = STAR_NUMBER + 1 
where POST_ID = :new.POST_ID; 

end; 
/
ALTER TRIGGER "C##STY"."STAR_TRIGGER" ENABLE; 
-------------------------------------------------------- 
-- DDL for Trigger STAR_DELETE_TRIGGER 
-------------------------------------------------------- 

    CREATE OR REPLACE EDITIONABLE TRIGGER "C##STY"."STAR_DELETE_TRIGGER" before delete on PS_STAR 
referencing new as new old as old 
for each row 
begin 
update PS_POST 
set 
STAR_NUMBER = STAR_NUMBER - 1 
where POST_ID = :old.POST_ID; 

end; 
/
ALTER TRIGGER "C##STY"."STAR_DELETE_TRIGGER" ENABLE; 
-------------------------------------------------------- 
-- Constraints for Table PS_STAR 
-------------------------------------------------------- 

    ALTER TABLE "C##STY"."PS_STAR" ADD CONSTRAINT "STAR_PK" PRIMARY KEY ("USER_ID", "POST_ID") 
    ENABLE; 
    ALTER TABLE "C##STY"."PS_STAR" MODIFY ("POST_ID" NOT NULL ENABLE); 
    ALTER TABLE "C##STY"."PS_STAR" MODIFY ("USER_ID" NOT NULL ENABLE); 

그래서 나는 게시물이 삭제 될 때 삭제 CASCADE 할 PS_STAR을 설정합니다. 스타 레코드가 삭제 될 때 PS_POST 테이블에서 STAR_NUMBER를 업데이트 할 수있는 트리거가 있습니다. PS_POST에서 항목을 삭제하려고하면 PS_STAR의 트리거가 제대로 작동하지 않아서 오류가 발생하는 것 같습니다.

ORA-04091: C##STY.PS_POST is mutating, trigger/function may not see it 

어떻게 해결할 수 있습니까? 미리 감사드립니다.

+0

DDL에서 관련이없는 많은 저장 절을 잘라내어보다 쉽게 ​​읽을 수 있습니다. 이제 거기에 전혀 외래 키 정의가없는 것 같습니다. 다시 돌아가서 확인했는데, 편집하기 전에 거기에 없었습니다! –

답변

1

비정규 화 된 STAR_NUMBER 열을 PS_POST 테이블에서 제거하고 트리거를 삭제하는 것이 가장 좋은 계획 일 수 있습니다. 번호가 필요할 때 PS_STAR 레코드의 수를 계산할 수 있습니다. 일부 극단적 인 경우를 제외하고는 "성능 비정규 화"는 거의 정당화되지 않습니다.

그러나 실제로 유지해야하는 경우 트리거가 레코드를 삭제할 때 트리거가 PS_POST 테이블을 업데이트하지 못하도록해야합니다. 이제 START_DELETE_TRIGGER의 몸을 위해 수정

create or replace trigger ps_post_before_delete_stmt 
before delete on ps_post 
begin 
    ps_post_pkg.deleting_post := true; 
end; 

create or replace trigger ps_post_after_delete_stmt 
after delete on ps_post 
begin 
    ps_post_pkg.deleting_post := false; 
end; 

:

create or replace package ps_post_pkg is 
    deleting_post boolean default false; 
end; 

다음 - 레벨이 PS_POST에 트리거 두 문을 추가 :이 이런 식으로 전역 변수로 패키지를 사용하여 달성 할 수

begin 
    if not ps_post_pkg.deleting_post then 
    update PS_POST 
    set STAR_NUMBER = STAR_NUMBER - 1 
    where POST_ID = :old.POST_ID; 
    end if; 
end; 

이제 레코드가 PO_POST에서 삭제되면 삭제는 PS_STAR로 캐스케이드되지만 START_DELETE_TRIGGER는 n 방금 삭제 된 PS_POST 레코드를 업데이트하려고하지 마십시오.