2012-10-19 74 views
3

我希望我的表根據它的TEMPLATE_ID對它的「order by」列進行排序。 我希望這發生在插入(可能通過插入觸發器)。 例如,如果我運行下面的插入,我應該得到下面的表值。在插入時初始化子序列列值。 (Oracle)

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; 

它給我的錯誤是:「表TEM​​PLATE_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是按順序排列的,所以你可以使用它,如果它有幫助。

+0

鏡像:https://forums.oracle.com/forums/thread.jspa?threadID=2455159 – Joel

+0

你找到了這個問題的解決theadsafe? –

+0

我沒有找到線程安全解決方案。 – Joel

回答

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,如果你在一個語句插入多條記錄,但我不知道,如果它的工作原理是這樣和我目前無法測試它。我希望你能得到大致的想法。

+0

我改編了row_number部分,以使其具有正確的oracle語法,但是我得到這個編譯器錯誤:「ORA-30483:窗口函數在這裏是不允許的」 – Joel

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; 
/
上的Oracle論壇
+1

這不是「線程安全的」。如果兩個不同的會話嘗試同時插入相同的template_id,則兩個記錄都有可能獲得相同的order_by值。 – Joel