Sql 在触发器中使用Merge语句
这是我将用于触发器的表:Sql 在触发器中使用Merge语句,sql,oracle,triggers,merge,Sql,Oracle,Triggers,Merge,这是我将用于触发器的表: 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"
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\u user
如果id\u用户存在,则对该id\u用户的活动列进行更新,如果该id\u用户不存在,则应插入新行
我成功地创建了merge-into触发器。在将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.
你不能用扳机来做这件事。行级触发器通常不能查看或修改触发器所在表中的数据,这就是为什么在尝试在其中进行合并时会得到ORA-04091。如果您也尝试查询该表,则会出现错误。有些场景有变通方法,但我认为它们不适用于这种情况,即使有,也会使您的模式过于复杂 你不需要扳机。您应该使用合并而不是插入。这是一个更复杂的语句,需要更多的输入,但一旦它出现在应用程序中就不重要了 根据您的表结构,而不是:
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。您可能已经有了一个触发器,可以从序列中设置它,但这里我将从序列中手动设置它,因此如果它与您已有的冲突,您可能需要从插入零件中删除它
因此,如果我使用所示的值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
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
如果我再次运行它,但将active设置为0,则您会得到:
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
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
我在评论中提到了这一点,但我真的会认真地重新考虑,因为这会使代码更难读写。正如该文档所说,Oracle不建议对数据库对象名称使用带引号的标识符。似乎没有任何明显的理由将所有内容强制简化。当然,如果您正在创建一个新的模式,并且没有太多的现有对象需要担心,那么这会更容易…使用下面的代码
CREATE TABLE "grace_period" (
"id" NUMBER(11) PRIMARY KEY NOT NULL,
"id_user" NUMBER(20) NOT NULL,
"active" NUMBER(11)
);
插入宽限期
值1123,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;
它工作得很好。为什么不使用merge语句而不是insert语句呢?你为什么要用扳机?嗨Alex。。。我不知道我为什么尝试使用触发器:merge语句会是什么样子?。谢谢你看一眼;看看你是否能把一些东西放在一起,如果你不能让它起作用,把你的尝试添加到问题中,并可能更改标题。这就是我们得到的:合并到宽限期id用户、日期限制、活动t1,使用SELECT 456作为id用户,“2014-04-09 12:00:00”作为fecha,1在t1.id_用户=t2.id_用户匹配时从双t2激活,然后更新集t1.active=t2.active,不匹配时插入t1.id_用户,t1.date_限制,t1.active值t2.id_用户,t2.fecha,t2.active Regard请将其作为格式化代码添加到问题中,而不是作为注释。你想做什么就做什么?事实上,如果是的话,也许可以加上它作为答案;虽然最初的问题是关于触发器的,但我不太确定*8-不相关的注释-为什么要使用带引号的标识符?当你不这样做的时候,生活更容易,代码也更容易阅读。