3

我希望我的表根据它的 TEMPLATE_ID 对其“排序依据”列进行排序。我希望这发生在插入时(可能通过插入触发器)。例如,如果我运行以下插入,我应该得到下表中的值。

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:在“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 已排序,因此如果有帮助,您可以使用它。

4

2 回答 2

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;
/
于 2012-10-19T21:59:14.867 回答
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,但我不确定它是否以这种方式工作,我目前无法对其进行测试。我希望你能得到大致的想法。

于 2012-10-19T21:54:57.317 回答