oracle数据库在级联和触发器一起时遇到删除错误

oracle database meet delete error when having cascade and trigger together

我在数据库中有两个 table,一个是 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);

所以我设置PS_STAR在删除一个post时级联删除。我还有一个触发器来更新 PS_POST table 中的 STAR_NUMBER 当星记录是 deleted.The 当我尝试删除 PS_POST 中的项目时出现问题,它似乎 PS_STAR 中的触发器无法正常工作,它给了我错误:

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

如何解决?提前致谢。

可能最好的计划是从 PS_POST table 中删除 denormalised STAR_NUMBER 列并删除触发器 - 你可以当你需要数量时,只需计算 PS_STAR 条记录的数量即可。 "Denormalisation for performance" 很少有理由,除非在某些极端情况下。

但是,如果您确实需要保留它,则需要防止触发器在从中删除记录时尝试更新 PS_POST table。这可以使用一个包含全局变量的包来实现,如下所示:

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

然后在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 不会尝试更新已删除的 PS_POST 记录刚刚删除。