这是我将用于触发器的表格:
CREATE TABLE "grace_period" (
"id" NUMBER(11) PRIMARY KEY NOT NULL,
"id_user" NUMBER(20) NOT NULL,
"date_limit" DATE NOT NULL,
"active" NUMBER(11),
"created_at" DATE NOT NULL,
"updated_at" DATE
);
我想做的是在插入之前创建一个触发器,检查新条目是否已包含"id_user"
。
如果"id_user"
存在,则对"active"
列"id_user"
进行更新,如果不存在,则应插入新行。
我设法将合并创建为触发器(此触发器将被使用,直到我将merge语句集成到php代码中)但是我遇到以下错误:
CREATE OR REPLACE TRIGGER "user_grace_changes"
BEFORE INSERT ON "grace_period"
FOR EACH ROW
BEGIN
MERGE INTO "grace_period" t1
USING dual
ON (t1."id_user" = :new."id_user")
WHEN MATCHED THEN
UPDATE SET t1."active" = :new."active"
WHEN NOT MATCHED THEN
INSERT( t1."id_user", t1."date_limit", t1."active" )
VALUES( :new."id_user", :new."date_limit", :new."active" );
END;
insert into "grace_period" ("id_user","date_limit","active")
VALUES (333, sysdate, 1);
> Informe de error - Error SQL: ORA-04091: la tabla
> PLATAFORMA.grace_period está mutando, puede que el disparador/la
> función no puedan verla ORA-06512: en "PLATAFORMA.user_grace_changes",
> línea 3 ORA-04088: error durante la ejecución del disparador
> 'PLATAFORMA.user_grace_changes' ORA-06512: en
> "PLATAFORMA.user_grace_changes", línea 3 ORA-04088: error durante la
> ejecución del disparador 'PLATAFORMA.user_grace_changes'
> 04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
> *Cause: A trigger (or a user defined plsql function that is referenced in
> this statement) attempted to look at (or modify) a table that was
> in the middle of being modified by the statement which fired it.
> *Action: Rewrite the trigger (or function) so it does not read that table.
答案 0 :(得分:1)
您无法在触发器中执行此操作。行级触发器通常无法查看或修改触发器所在表中的数据,这就是当您尝试在其中执行merge
时获得ORA-04091的原因。如果您尝试查询表格,则会收到错误消息。某些方案有解决方法,但我不认为他们会为这种情况工作,并且即使他们这样做也会使您的架构过于复杂。
你不需要触发器。您应该使用merge
而不是 insert
。这是一个更复杂的陈述,需要更多的打字,但一旦它在一个不重要的应用程序中。
根据您的表格结构而不是:
insert into "grace_period" ("id_user","date_limit","active")
VALUES (333, sysdate, 1);
你可以这样做:
MERGE INTO "grace_period" target
USING (
SELECT 333 AS "id_user", sysdate AS "date_limit", 1 AS "active" FROM dual
) source
ON (target."id_user" = source."id_user")
WHEN MATCHED THEN
UPDATE SET target."active" = source."active", "updated_at" = sysdate
WHEN NOT MATCHED THEN
INSERT("id", "id_user", "date_limit", "active", "created_at")
VALUES("grace_seq".NEXTVAL, source."id_user", source."date_limit",
source."active", sysdate);
您传递给insert
的值现在是using
子句中的伪列,从伪表dual
中选择。然后将它们与真实表格中的现有记录进行比较。如果找到匹配则更新;否则它被插入。
我猜你想要自动设置created_at
和updated_at
,还要设置主键id
。您可能已经有一个触发器可以从序列中设置它,但是我在这里从序列手动设置它,因此如果它与您已有的部分冲突,您可能需要从insert
部分删除它
因此,如果我使用显示的值333,sysdate,1
运行它,那么你得到:
1 rows merged.
select * from "grace_period";
id id_user date_limit active created_at updated_at
---------- ---------- ---------- ---------- ---------- ----------
1 333 09-APR-14 1 09-APR-14
如果我再次运行它,但active
设置为0,那么你得到:
1 rows merged.
select * from "grace_period";
id id_user date_limit active created_at updated_at
---------- ---------- ---------- ---------- ---------- ----------
1 333 09-APR-14 0 09-APR-14 09-APR-14
如果你想让它更容易打电话,你可以把它包装在一个程序中:
create procedure merge_grace (p_id_user "grace_period"."id_user"%type,
p_date_limit "grace_period"."date_limit"%type,
p_active "grace_period"."active"%type) as
begin
merge into "grace_period" target
using (
select p_id_user as "id_user", p_date_limit as "date_limit",
p_active as "active"
from dual
) source
on (target."id_user" = source."id_user")
when matched then
update set target."active" = source."active", "updated_at" = sysdate
when not matched then
insert("id", "id_user", "date_limit", "active", "created_at")
values("grace_seq".nextval, source."id_user", source."date_limit",
source."active", sysdate);
end;
/
然后调用它更友好:
exec merge_grace(333, sysdate, 1);
anonymous block completed
select * from "grace_period";
id id_user date_limit active created_at updated_at
---------- ---------- ---------- ---------- ---------- ----------
3 333 09-APR-14 1 09-APR-14
exec merge_grace(333, sysdate, 0);
anonymous block completed
select * from "grace_period";
id id_user date_limit active created_at updated_at
---------- ---------- ---------- ---------- ---------- ----------
3 333 09-APR-14 0 09-APR-14 09-APR-14
我在评论中提到了这一点,但我真的会认真地重新考虑using quoted identifiers,因为它们使代码更难以读写。正如该文档所述,Oracle不建议对数据库对象名称使用带引号的标识符。似乎没有任何明显的理由让一切都被迫小写。当然,如果您正在创建新架构并且不需要担心很多现有对象,这会更容易......
答案 1 :(得分:0)
使用以下代码..
CREATE TABLE "grace_period" (
"id" NUMBER(11) PRIMARY KEY NOT NULL,
"id_user" NUMBER(20) NOT NULL,
"active" NUMBER(11)
);
插入grace_period 值(1,123,1)
create or replace PROCEDURE insert_on_grace(
p_id NUMBER,
p_id_user NUMBER,
p_active NUMBER)
AS
BEGIN
INSERT INTO grace_period VALUES
(p_id,p_id_user,p_active);
EXCEPTION
WHEN dup_val_on_index THEN
UPDATE grace_period
SET "active" =1
WHERE "id" =p_id ;
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'Erros');
END;
它工作得很好。