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は変化しています、トリガー/関数はそれを見ることができないかもしれません」

したがって、このトリガーを作成する別の方法が必要です。また、これら2つの挿入が異なるセッションで同時に発生した場合でも、結果のレコードが異なる「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:トリガーでCOMMITできません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

これが機能しない理由は、それ自体の行レベルのトリガーでテーブルを選択または更新できないためです。あなたができることは、テーブルレベルのトリガーを書くことです:

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;

1つのステートメントに複数のレコードを挿入すると、レコードが同じORDER_BYになるのを防ぐために追加row_numberしましたが、そのように機能するかどうかはわかりません。現時点ではテストできません。一般的な考え方を理解していただければ幸いです。

于 2012-10-19T21:54:57.317 に答える