2013-03-21 52 views
1

我創建了一個只允許用戶有10個當前下訂單的觸發器。所以現在當客戶試圖放置第11號訂單時,oracle數據庫會拋出一個錯誤。那麼3錯誤。PL/SQL Oracle錯誤處理

ORA-20000:您目前有10個或更多的訂單處理。

ORA-06512:在 「C3283535.TRG_ORDER_LIMIT」,第12行

ORA-04088:觸發的執行期間錯誤 'C3283535.TRG_ORDER_LIMIT'

頂端錯誤是一個我已經創建使用:

raise_application_error(-20000,'您目前有10個或更多訂單 處理'。

我只是想知道搜索後,並嘗試了很多方法如何更改其他兩個錯誤的錯誤信息,甚至不顯示它們都一起給用戶?

這裏是我使用的代碼

create or replace trigger trg_order_limit 
    before insert on placed_order for each row 
    declare 
    v_count number; 
    begin 
    -- Get current order count 
    select count(order_id) 
    into v_count 
    from placed_order 
    where fk1_customer_id = :new.fk1_customer_id; 

    -- Raise exception if there are too many 
    if v_count >= 10 then 
    EXCEPTION 
    WHEN OTHERS THEN 
    raise_application_error(-20000, 'You currently have 10 or more orders processing.'); 
    end if; 
    end; 

非常感謝 理查德

+0

不喜歡觸發器的另一個原因。你可以用你的insert語句包裝一個程序包/程序調用,將其捕獲並返回一個錯誤信息給領子?您不會說明該語句執行的編程環境。你可以過濾調用應用程序中的錯誤嗎? – OldProgrammer 2013-03-21 13:05:15

+0

我確信這個問題在其他地方得到了解答...我找不到它:-(。 – Ben 2013-03-21 13:07:12

+1

[嘆氣...](http://stackoverflow.com/a/15546744/876937):As我說過,異常需要被你的應用程序所捕獲,例如,如果你使用OCI編寫PHP,你可以使用'oci_error'函數,看看'ORA-20000'錯誤[I]是否指定 – Xophmeister 2013-03-21 13:07:19

回答

4

的異常傳播進入從內部到外部的塊,而不是從外部進入的變量範圍到內部塊。關於這方面的更多參考,請閱讀McLaughlin的「用PL/SQL編程」,第5章。

你在這裏得到的是一個異常堆棧 - 從最內層塊到最外層塊引發的異常。

當您從觸發器中引發異常時,您的raise_application_error語句返回錯誤。

然後它被傳播到說ORA-06512: at "C3283535.TRG_ORDER_LIMIT", line 12的觸發塊。這是因爲觸發器將引發的異常視爲錯誤並停止繼續。

該錯誤然後傳播到引發ORA-04088: error during execution of trigger 'C3283535.TRG_ORDER_LIMIT'的會話。這個錯誤會向我們報告錯誤在哪裏發生,如程序的哪一部分。

如果您使用的是Java Server Pages或PHP等前端程序,您將首先發現引發的錯誤--20000。所以,你可以顯示給你的最終用戶。

編輯:

關於第一個錯誤 - ORA-20000,您可以在RAISE_APPLICATION_ERROR語句本身改變它。

如果您想要處理ORA-06512,您可以使用Uday Shankar的答案,這有助於處理此錯誤並顯示相應的錯誤消息。

但是,你仍然會得到最後的ORA-04088。如果我在你的位置,我不會擔心,因爲在獲得ORA-20000之後,我會在前端自己提出應用程序錯誤,同時隱藏用戶的所有其他詳細信息。

其實這個這個oracle的異常棧的本質。從最內層到最外層的所有錯誤都會被提升。這對我們確定錯誤的確切來源很有幫助。

+0

嗯我明白你的意思,但我只是在oracle sql命令中使用sql代碼 – 2013-03-21 13:15:10

+0

是的,我明白了,但是你的SQL命令執行了一個觸發器,這就是整個異常堆棧進入隊伍的地方。 – Rachcha 2013-03-21 13:16:16

+2

第一個錯誤提出從你的前端可以捕捉到的是你在觸發器中使用'raise_application_error'引發的錯誤。至少,我認爲你至少可以忍受,整個世界都可以! – Rachcha 2013-03-21 13:18:19

2

在觸發器可以添加例外處理部分如下圖所示:

EXCEPTION 
    WHEN OTHERS THEN 
     raise_application_error(-20000, 'You currently have 10 or more orders processing.'); 
0

我看,這是一個很老的文章,但我認爲讀者應該知道,

  1. 這並沒有真正執行業務規則(最多10個訂單)。如果 是隻是「一些」號碼,以避免過高的金額,你不要 如果有時人們有12個訂單,那麼這可能是好的。但是,如果沒有,請考慮已經有9個訂單的場景,然後同時爲2個不同的會話/交易插入同一個客戶的訂單。在這種情況下,您最終將獲得11個訂單,而不會檢測到這種溢出情況。所以你不能依靠這個觸發器。
  2. 除此之外,如果可以更新fk1_customer_id,則可能還需要在更新時觸發此觸發器(我已經看到了實現方式,首先將NULL放入FK列中,稍後更新爲實際值) 。您可能需要考慮這種情況是否現實。
  3. 觸發器存在根本缺陷。你在一個事務中,並且在一個當前正在執行但尚未完成的語句中。那麼如果插入不是單行插入,而是像 insert into placed_order (select ... from waiting_orders ...) 那你期望觸發器看到什麼?

這種業務規則不易實施。但是,如果您選擇在觸發器中執行此操作,最好在after語句觸發器中執行此操作(因此,不要在before行觸發器中執行)。 after語句觸發器仍然不會看到其他未提交事務的結果,但至少當前語句處於已定義狀態。

事實上,業務規則基本上只能在提交時執行;但在Oracle數據庫中沒有像ON-COMMIT觸發器那樣的東西。 您可以執行的操作是將記錄數記錄到客戶表中(添加一列ORDER_COUNT),並在該表中放置一個延遲約束(ORDER_COUNT < = 10)。 但是,你仍然依靠在你的代碼中正確保持這個字段。

一個完全可靠的替代,但稍顯麻煩,是對物化視圖的placed_order表中創建一個物化視圖(類似於SELECT fk_customer_id, count(*) order_count from placed_orders group by fk_customer_id,與FAST REFRESH ON COMMIT並創建一個檢查約束ORDER_COUNT < = 10。 這是一個關於該唯一方式來可靠地執行這種類型的約束,而不必考慮併發會話,更新等所有可能的情況。 但請注意,FAST REFRESH ON COMMIT會減慢提交速度;所以此解決方案不適用於大容量(嘆息...爲什麼只是Oracle不提供ON COMMIT觸發器...)