2009-09-22 26 views
1

我想用另一列的聚合自動更新數據庫列。 有三個表涉及:自動更新數據庫中的字段

T_RIDER 
    RIDER_ID 
    TMP_PONYLIST 
    ... 

T_RIDER_PONY 
    RIDER_ID 
    PONY_ID 

T_PONY 
    PONY_ID 
    PONY_NAME 
    ... 

T_RIDERT_PONY具有N:經由T_RIDER_PONY關係。 T_RIDERT_PONY有一些更多的列,但只有TMP_PONYLISTPONY_NAME在這裏是相關的。

TMP_PONYLIST是一個分號散列表PONY_NAMES,想象像"Twisty Tail;Candy Cane;Lucky Leaf"。 無論T_RIDER_PONYT_PONY發生了什麼,我都希望保持此字段爲最新。

我的第一個想法是觸發T_RIDER_PONYT_PONY。 問題是,它似乎是不可能讀取觸發器內的T_RIDER_PONY,我總是得到ORA-04091。我發現了一些有關使用三個觸發器和包變量的提示,但這聽起來太複雜了。

也許你認爲我最好改變模式或完全擺脫TMP_PONYLIST。 這些是選項,但不是這個問題的主題。 目前我只對不需要對我的應用程序進行任何更改的答案感興趣(沒有應用程序直接與表格一起工作,只有視圖,允許有視圖的詭計)。

那麼,我該如何自動保持TMP_PONYLIST最新? 如何連接字符串是一個有趣的子問題,我還沒有找到一個優雅的解決方案。

我正在使用Oracle數據庫10g企業版版本10.2.0.4.0 - 64bi。

UPDATE

我喜歡用物化視圖的概念。 我所擁有的是:

CREATE MATERIALIZED VIEW 
    V_TMP_PONYLIST 
BUILD IMMEDIATE 
REFRESH COMPLETE ON COMMIT 
AS SELECT 
    R.RIDER_ID, string_agg(P.PONY_NAME) AS TMP_PONYLIST 
FROM 
    T_PONY P, T_RIDER R, T_RIDER_PONY RP 
WHERE 
    P.PLOTGROUP_ID=RP.PLOTGROUP_ID AND 
    R.QUEUE_ID=RP.QUEUE_ID 
GROUP BY R.RIDER_ID; 

string_agg沒有顯示,因爲它很長,我認爲這是不相關的。

它不會編譯與ON COMMIT,我得到ORA-12054。 據我所知只有 REFRESH FAST禁止文檔聚合,所以這裏有什麼問題?

UPDATE Vincents和Tonys的答案是不同的,但都有幫助。 我接受Tonys,但一定要閱讀Vincents的答案。

+1

我建議不要在單個列中存儲多個值(「Twisty Tail; Candy Cane; Lucky Leaf」)... – 2009-09-22 11:24:48

回答

1

爲什麼你需要讀取觸發器中的T_RIDER_PONY表?你會要麼插入或刪除一行,所以所有的觸發需要做的是在T_PONY和更新表T_RIDER擡頭看小馬的名字,要麼將名稱或從TMP_PONYLIST這樣

create trigger t_rider_pony_trg 
after insert or delete on t_rider_pony 
for each row 
begin 
    if inserting then 
     select pony_name 
     into l_pony_name 
     where pony_id = :new.pony_id; 

     update t_rider 
     set tmp_ponylist = tmp_ponylist || ';' || l_pony_name 
     where rider_id = :new.rider_id; 
    elsif deleting then 
     select pony_name 
     into l_pony_name 
     where pony_id = :old.pony_id; 

     update t_rider 
     set tmp_ponylist = ltrim ( 
           rtrim ( 
            replace(';' || tmp_ponylist || ';', 
              ';' || l_pony_name || ';', 
              ';'), 
            ';', ';') 
     where rider_id = :old.rider_id; 
    end if; 
end; 

取出那我承認刪除部分中的更新是相當不愉快的;最好使用像apex_util.string_to_table和apex_util.table_to_string這樣的工具來處理它!詳情請參閱this SO answer

+0

+1很酷的解決方案,我認爲字符串操作太複雜了,但是您的解決方案非常好。目前tmp_ponylist已排序。你認爲擴展你的解決方案難以保持tmp_ponylist排序嗎? – bbuser 2009-09-22 12:15:12

+0

可以對列表進行排序,但我認爲您需要使用臨時表或用戶定義類型來存儲它並按順序選擇。或者自己編寫一些冒泡排序代碼來處理PL/SQL集合! – 2009-09-22 13:30:23

+0

我不認爲這將在多用戶環境中工作。 用戶A插入一行觸發器更新T_Rider; 用戶B插入一行,沒有看到A的變化; A塊B的更新; A提交,釋放鎖定,B更新; B提交,A的更改不見了 – 2009-09-22 13:36:00

3

TMP_PONYLIST上的信息是多餘的(它存在於其他地方)。你會遇到各種各樣的問題來維護它(沒有解決方案將在多用戶環境中正常工作,除非有某種鎖定機制)。

在標準化模型中,您可以簡單地從物理模型中刪除此列。如果你需要的信息,你可以使用一個視圖,例如與Oracle 11gR2中:

CREATE OR REPLACE VIEW rider_v AS 
SELECT rider_id, /*...,*/ 
     (SELECT listagg(p.pony_name, ';') WITHIN GROUP (ORDER BY p.pony_name) 
      FROM t_pony p 
      JOIN t_rider_pony rp ON (p.pony_id = rp.pony_id) 
     WHERE rp.rider_id = r.rider_id) tmp_ponylist 
    FROM t_rider r; 

例如11gR2的前串聚集見this SO

+0

由於性能原因引入了TMP_PONYLIST。我們可能會擺脫TMP_PONYLIST,但我仍在探索其他選項。我喜歡使用視圖的想法,但它需要成爲一個物化視圖。看到我的問題更新。 – bbuser 2009-09-23 08:21:57