我希望我的表根据它的TEMPLATE_ID对其“order by”列进行排序。 我希望这可以在插入时发生(可能通过插入触发器)。 例如,如果我运行以下插入,我应该得到以下表值。
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (1, 1)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (2, 1)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (3, 1)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (4, 2)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (5, 2)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (6, 2)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (7, 2)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (8, 3)
ID TEMPLATE_ID ORDER_BY
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
7 2 4
8 3 1
我首先尝试创建此触发器,但插入时会出现错误。
create or replace
trigger TEMPLATE_ATTRIBUTES_AF_INS_TRIG
after insert on TEMPLATE_ATTRIBUTES
for each row
begin
if :NEW.ORDER_BY is null then
update TEMPLATE_ATTRIBUTES
set ORDER_BY = (select coalesce(MAX(ta.ORDER_BY), 0) + 1 from TEMPLATE_ATTRIBUTES ta where ta.TEMPLATE_ID = :NEW.TEMPLATE_ID)
where ID = :NEW.ID;
end if;
end;
它给我的错误是:“表TEMPLATE_ATTRIBUTES正在变异,触发/功能可能看不到它”
所以我需要一种不同的方法来构建这个触发器。而且我还需要它“线程安全”,这样如果这两个插入同时出现在不同的会话上,那么结果记录仍会得到不同的“ORDER_BY”值:
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (1, 1)
INSERT INTO TEMPLATE_ATTRIBUTES (ID, TEMPLATE_ID) VALUES (2, 1)
编辑:
我尝试了常见的工作,因为“表正在变异,触发器/功能可能看不到它”,而且工作“工作”但它不是“线程安全的”。我试图添加锁定,但它在插入
时给了我另一个错误create or replace package state_pkg
as
type ridArray is table of rowid index by binary_integer;
newRows ridArray;
empty ridArray;
end;
create or replace trigger TEMPLATE_ATTRIBUTES_ORDER_BY_TB4
before insert on TEMPLATE_ATTRIBUTES
begin
state_pkg.newRows := state_pkg.empty;
end;
create or replace trigger TEMPLATE_ATTRIBUTES_ORDER_BY_TAF1
after insert on TEMPLATE_ATTRIBUTES for each row
begin
if :NEW.ORDER_BY is null then
state_pkg.newRows( state_pkg.newRows.count+1 ) := :new.rowid;
end if;
end;
create or replace trigger TEMPLATE_ATTRIBUTES_ORDER_BY_TAF2
after insert on TEMPLATE_ATTRIBUTES
declare
v_request number;
v_lockhandle varchar2(200);
begin
dbms_lock.allocate_unique('TEMPLATE_ATTRIBUTES_ORDER_BY_lock', v_lockhandle);
while v_request <> 0 loop
v_request:= dbms_lock.request(v_lockhandle, dbms_lock.x_mode);
end loop;
begin
for i in 1 .. state_pkg.newRows.count loop
update TEMPLATE_ATTRIBUTES
set ORDER_BY = (select coalesce(MAX(q.ORDER_BY), 0) + 1 from TEMPLATE_ATTRIBUTES q where q.TEMPLATE_ID = (select q2.TEMPLATE_ID from TEMPLATE_ATTRIBUTES q2 where q2.rowid = state_pkg.newRows(i)))
where rowid = state_pkg.newRows(i);
end loop;
v_request:= dbms_lock.release(v_lockhandle);
EXCEPTION WHEN OTHERS THEN
v_request:= dbms_lock.release(v_lockhandle);
raise;
end;
end;
这给了我:
ORA-04092:无法在触发器ORA-06512中进行COMMIT:在“SYS.DBMS_LOCK”,第250行ORA-06512:在“TEMPLATE_ATTRIBUTES_ORDER_BY_TAF2”,第5行ORA-04088:执行触发器'TEMPLATE_ATTRIBUTES_ORDER_BY_TAF2'时出错ORA- 06512
编辑2: ORDER_BY列必须是可更新列。 ID实际上使用序列并在插入触发器之前设置其值。当我将它包含在插入示例中时,我认为我正在简化我的问题,但这是不正确的。 ORDER_BY的初始值与ID无关,而是与记录的插入顺序有关。但是ID已按顺序排列,因此如果有帮助,您可以使用它。
答案 0 :(得分:0)
这不起作用的原因,它不能从它自己的行级触发器中选择或更新表。你可以做的是写一个表级触发器:
create or replace trigger TEMPLATE_ATTRIBUTES_AF_INS_TRIG
after insert on TEMPLATE_ATTRIBUTES
begin
update TEMPLATE_ATTRIBUTES a
set
a.ORDER_BY =
(select
coalesce(MAX(ta.ORDER_BY), 0)
from
TEMPLATE_ATTRIBUTES ta
where
ta.TEMPLATE_ID = a.TEMPLATE_ID) + row_number
where
a.ORDER_BY is null;
end;
如果您在一个语句中插入多条记录,我添加了row_number
以防止记录获得相同的ORDER_BY,但我不确定它是否以这种方式工作,此刻我无法测试它。我希望你能得到一般的想法。
答案 1 :(得分:0)
使用更新触发器更新同一个表可能会导致逻辑循环,从而导致变异表错误。尝试使用之前插入触发器并设置order_by的最大值+ 1。
create or replace
trigger TEMPLATE_ATTRIBUTES_AF_INS_TRIG
before insert on TEMPLATE_ATTRIBUTES
for each row
begin
if :NEW.ORDER_BY is null then
select max(order_by) + 1 into :new.order_by
from template_attributes
where template_id = :new.template_id;
end if;
:new.order_by := nvl(:new.order_by,1); -- For new templates
end;
/