2017-09-14 4 views
0

データベースに2つのテーブルがあり、1つはPS_POSTで、もう1つはPS_STARです。ここでカスケードとトリガーを一緒に使用すると、Oracleデータベースが削除エラーに遭遇する

が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); 

だから私は、ポストが削除されたときに削除カスケード接続することがPS_STARを設定します。 PS_POSTでアイテムを削除しようとすると、PS_STARのトリガーが正常に動作せず、エラーが出るようです。

この問題は、スターレコードが削除されたときにPS_POSTテーブルのSTAR_NUMBERを更新するトリガーがあります。
ORA-04091: C##STY.PS_POST is mutating, trigger/function may not see it 

これを解決するにはどうすればよいですか。

+0

私はあなたのDDLから無関係の記憶節などをたくさんカットして、より簡単に読むことができます。今では、そこに外来キーの定義がないようです - 私は戻ってチェックしました。 –

答えて

1

はおそらく最良の計画では、がPS_POSTテーブルから STAR_NUMBER列をdenormalised削除し、トリガーをドロップすることです - あなたは数を必要とするとき、あなただけのPS_STARレコードの数をカウントすることができます。極端な場合を除いて、「パフォーマンスの非正規化」はほとんど正当化されません。

ただし、実際に保存する必要がある場合は、レコードが削除されるときに、トリガーがPS_POSTテーブルを更新しようとしないようにする必要があります。これは、このようなことでグローバル変数とパッケージを使用して達成することができます。

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

次に、2つのステートメントを追加レベルのはPS_POSTにトリガー:

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; 

今すぐにあなたのSTART_DELETE_TRIGGERの体を修正:

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レコードを更新しようとしないでください。

関連する問題