2016-07-21 153 views
0

我創建了2個表格:INFORMATIONFEED如何讓列的值自動反映,當值插入/更新/刪除/從同一個表中的另一列?

INFORMATION has 2 attributes : ID(Primary Key), TOT_AMOUNT. 

FEED has 4 attributes : ID(Foreign key refer INFORMATION(ID)), S_AMOUNT, S_DATE, TOT_REM. 

現在,我要插入/更新/從TOT_REM刪除值/的基礎上,S_AMOUNTTOT_AMOUNT的插入/刪除/更新。

樣本內容是:

INFORMATION Table 
------------------ 
ID | TOT_AMOUNT 
1 | 100 
2 | 20 
3 | 50 
... 

       FEED Table 
---------------------------------------- 
ID | S_AMOUNT | S_DATE | TOT_REM 
1 |  10  |10.10.2010| 90 
1 |  10  |13.10.2010| 80 
1 |  30  |17.10.2013| 50 
1 |  10  |20.10.2016| 40 
... 

我們需要自動插入值到TOT_REM屬性的基礎上,更新/插入/上S_AMOUNT進行操作刪除,與TOT_AMOUNT & S_AMOUNT幫助。

在任何時候,TOT_REM不能小於0。而且,TOT_REM需要自動插入/刪除/更新,使得

TOT_REM for i(at a specific date) = (TOT_AMOUNT for ID=i) - 
            SUM(S_AMOUNT of all instances of ID=i, 
            which is later than the S_DATE for ID=i); 

因此,假設如果我們刪除第二元組(1, 10,'13 .10.2010' ,80),的BR_FEED反射狀態應該是:

   FEED Table 
---------------------------------------- 
ID | S_AMOUNT | S_DATE | TOT_REM 
1 |  10  |10.10.2010| 90 
1 |  30  |17.10.2013| 60 
1 |  10  |20.10.2016| 50 
... 

我寫了一個觸發器,其失敗表示

ORA-04091: table SSUMAN.FEED is mutating, trigger/function may not see it 

代碼觸發是:

CREATE OR REPLACE TRIGGER BR_INSERT_TRB 
AFTER DELETE OR INSERT OR UPDATE OF S_AMOUNT ON FEED 
FOR EACH ROW 
BEGIN 

IF DELETING THEN 
UPDATE FEED bf 
SET  bf.TOT_REM = bf.S_AMOUNT + :OLD.S_AMOUNT; 
END IF; 

IF INSERTING THEN 
INSERT INTO FEED (TOT_REM) VALUES(
((SELECT TOT_AMOUNT FROM INFORMATION bi WHERE bi.ID=:NEW.ID) - 
(SELECT SUM(S_AMOUNT) FROM FEED bf where bf.ID=:NEW.ID) - 
:NEW.S_AMOUNT); 
END IF; 

IF UPDATING THEN 
UPDATE FEED bf 
SET  bf.TOT_REM = (SELECT TOT_AMOUNT FROM BR_INFORMATION bi WHERE bi.ID=bf.ID) - 
(SELECT SUM(S_AMOUNT) FROM FEED bf where bf.ID=:NEW.ID) - 
:NEW.S_AMOUNT 
WHERE :NEW.ID IS NOT NULL; 
END IF; 

END; 

問題:

  1. 是這種做法有缺陷?我可以通過這種方式達到我想要的效果嗎?[可選]
  2. 是否有任何將視圖帶到這裏的範圍?我無法想象那一行!也許,缺乏經驗...... [可選]
  3. 任何更好的方法,讓TOT_REM值可以自動反映?[強制來回答]
+0

當你說「TOT_REM不能比0少」 - 你的意思是,作爲計算TOT_REM邏輯的一部分,還是應該有問題的插入/更新/刪除被拒絕,因爲它會導致TOT_REM變得消極?這兩者完全不同,並以不同的方式解決。 – mathguy

+0

@mathguy - 應該拒絕,謝謝澄清。任何其他細節需要? –

+0

我在這裏看到一些困難。最大的是開始時對TOT_REM的計算。如果刪除10.10.2010行,則以下行中的TOT_REM需要從INFORMATION表中讀取,而不是從其上面的行中讀取。如果您在第一個(2010年10月10日之前)之上添加一行,則10.10.2010 TOT_ROM將需要根據上面的行進行計算,而不是從INFORMATION中計算。更大的問題仍然是:你拒絕一行,因爲它會使TOT_ROM成爲負面。稍後,您會刪除一個包含更舊日期的行......現在您需要先將您拒絕的行取回嗎? – mathguy

回答

1

我覺得這是更好地創建視圖。看看這個

測試數據

create table feed(ID,S_AMOUNT,S_DATE) as (
    SELECT 1,10, TO_DATE('10.10.2010','dd.mm.yyyy') FROM dual UNION all 
    SELECT 1,10,TO_DATE('13.10.2010','dd.mm.yyyy') FROM dual UNION all 
    SELECT 1,30,TO_DATE('17.10.2013','dd.mm.yyyy') FROM dual UNION all 
    SELECT 2,10,TO_DATE('20.10.2016','dd.mm.yyyy') FROM dual) 

create table INFORMATION (id, TOT_AMOUNT) as (
    SELECT 1,100 FROM DUAL UNION ALL 
    SELECT 2,20 FROM DUAL UNION ALL 
    SELECT 3,50 FROM DUAL) 

查詢

create or replace view result_feed as 
    SELECT f.*,i.TOT_AMOUNT - NVL(SUM(S_AMOUNT) OVER(PARTITION BY f.ID ORDER BY f.S_DATE),0) AS tot_rem FROM FEED f, INFORMATION i 
    WHERE f.ID = i.id 
    ORDER BY f.ID, f.S_DATE; 
    -- used NVL to prevent side-effect of null values 


    SELECT * from RESULT_FEED; 

你的方法與觸發不適合在這種情況下。我認爲很少添加數據,只有在特殊情況下才需要查詢。當然,有些方法可以解決變異表(包變量,複合觸發器,自動事務),但我認爲它們只會將性能問題添加到數據庫中。

+0

謝謝,這變成了有用的,並解決了我的問題。任何理由爲什麼不使用虛擬列,並偏好查看? –

+0

@Am_I_Helpful在這種情況下,我不使用虛擬列,因爲它是禁止使用的分析函數(我不知道如何在沒有分析函數的情況下實現您的邏輯)。未來:如果在某些複雜查詢中將謂詞用作謂詞,或者將它用作約束或出於某種目的,我需要使用虛擬列索引。一些開發人員不喜歡意見,因爲他們認爲他們的業務具有複雜的邏輯,更好地使用表而不是視圖。一般情況下,由於某些報告或用戶數據的抽象結果,我使用了視圖。 –

1

如果這是我的業務問題,我可以從頭開始(這可能不是你的情況可能),我會保持INFORMATION表原樣,我就從FEED刪除TOT_REM列,我會創建一個看起來像當前FEED表的視圖。您可以在視圖定義中編寫所有必要的邏輯。

ADDED

首先,這裏是一個視圖定義;它假定INFORMATIONFEED如由OP所描述的,在沒有FEEDTOT_REM基表。

create view remaining_balance (id, s_amount, s_date, tot_rem) as 
select i.id, f.s_amount, f.s_date, 
     i.tot_amount - nvl(sum(f.s_amount) over (partition by f.id order by f.s_date), 0) 
from information i left outer join feed f 
        on i.id = f.id 
; 

視圖使用一個外連接,以包含來自不具有在FEED任何相應行INFORMATIONid的。 (然後,處理空值在TOT_REM計算,我用的是nvl()功能NULL轉換爲0。)

下面是運行該視圖的例子:

SQL> select * from information; 

     ID TOT_AMOUNT 
---------- ---------- 
     1  100 
     2   20 
     3   50 

3 rows selected. 

SQL> select * from feed; 

     ID S_AMOUNT S_DATE 
---------- ---------- ---------- 
     1   10 2010-10-10 
     1   10 2010-10-13 
     1   30 2010-10-17 
     1   10 2016-10-20 

4 rows selected. 

SQL> select * from remaining_balance order by id, s_date; 

     ID S_AMOUNT S_DATE  TOT_REM 
---------- ---------- ---------- ---------- 
     1   10 2010-10-10   90 
     1   10 2010-10-13   80 
     1   30 2010-10-17   50 
     1   10 2016-10-20   40 
     2        20 
     3        50 

6 rows selected. 

現在,一個良好建立強制執行複雜約束的方法是使用物化視圖。直接檢查限制只適用於行級,並且當條件涉及多個表時不能使用。在當前問題的檢查是對兩個表,並TOT_REM依賴於其他行的FEED表 - 所以在FEED表的約束不會反正工作。

物化視圖方法是定義一個像我創建的視圖作爲物化視圖,用refresh fast on commit來定義它(以便在基表上的DML操作之後立即檢查約束),並創建一個檢查物化視圖的約束。在這個問題上,這將是對TOT_RM >= 0的檢查。

當視圖定義使用分析函數時,唉,refresh fast on commit被禁止(至少在甲骨文版本11.2,這是我最近)。我用sum()函數的解析版本,所以這是不行的。

這似乎是有道理的,但是,定義不同的物化視圖,如下圖所示:

create materialized view remaining_balance (id, tot_rem) as 
select i.id, i.tot_amount - f.sum_s_amount 
from information i inner join (select id, sum(s_amount) as sum_s_amount 
           from  feed 
           group by id) f 
        on i.id = f.id 
; 

SQL> select * from remaining_balance; 

     ID TOT_REM 
---------- ---------- 
     1   40 

我不使用外再加入,因爲這種情況應該只是一個顯示剩餘款項。我假設INFORMATIONTOT_AMOUNT有一個檢查約束,以確保它不是負數,並且FEED中的id是指向INFORMATION中的id的主鍵,因此此版本的視圖沒有外連接顯示的附加信息。 (然而,如果需要,它可以被製成包括所有id「S)。

在這裏你應該能夠定義與refresh fast on commit視圖,並添加檢查約束的效果tot_rem >= 0。唉,我無法測試它;高級複製(創建所需的物化視圖日誌,而這又是需要refresh fast)不可用/甲骨文的免費的Express版本,我已經啓用。嘗試嘗試這一點 - 但這可能是您需要的解決方案。祝你好運!

+0

你可以擴展視圖定義(代碼)嗎?我無法爲視圖設計定義。 –

+0

我可以,但如果您需要的確實是一種約束,而不僅僅是一種報告解決方案,它將無濟於事。 – mathguy

+0

這的確是一個非常好的答案。不幸的是,它不包括s_date部分,因爲我無法實現(我在FEED表中想要的)。不管怎麼說,多謝拉。因爲你的這個答案而學習了幾件新事物。 –

相關問題