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 记录刚刚删除。
我在数据库中有两个 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 记录刚刚删除。