2014-09-26 134 views
0

我有以下表格:PL/SQL觸發器返回缺貨錯誤:觸發與編譯錯誤創建

order_lines table 
    Name Null? Type 
    ORDER_ID NOT NULL NUMBER(5) 
    PRODUCT_ID NOT NULL NUMBER(4) 
    ACTUAL_PRICE  NUMBER 
    DISCOUNT  NUMBER 
    QUANTITY  NUMBER 
    TOTAL_AMOUNT  NUMBER 

而且

Product_inventory table 
Name Null? Type 
PRODUCT_ID NOT NULL NUMBER(4) 
QTY_ON_HAND  NUMBER 
QTY_ON_ORDER  NUMBER 
DATE_ORDERED  DATE 
DELIVERY_DATE  DATE 

我想有一個觸發器,它檢查訂單的數量,看它是否缺貨,並顯示一條消息,如果是。我現在的觸發與一些錯誤編譯:

CREATE OR REPLACE TRIGGER check_order_line 
    BEFORE INSERT OR UPDATE ON order_lines 
    for each row 
DECLARE 
    l_current_stock product_inventory.qty_on_hand%type; 
BEGIN 
    select product_inventory.qty_on_hand 
    into l_current_stock 
    from product_inventory, order_lines 
    where product_inventory.product_id = :new.product_id; 
    if(:new.quantity > l_current_stock) then 
    RAISE_APPLICATION_ERROR(-20103, 'Insufficient Stock'); 
    else 
    update product_inventory 
    set qty_on_hand = qty_on_hand - :new.quantity 
where product_inventory.product_id = :new.product_id; 
    end if; 
END; 

當我測試了扳機,我發現了以下錯誤:

insert into order_lines values (388,1023,100,20,2,160) 
      * 

ERROR at line 1: 
ORA-01422: exact fetch returns more than requested number of rows 
ORA-06512: at "DBA643.CHECK_ORDER_LINE", line 4 
ORA-04088: error during execution of trigger 'DBA643.CHECK_ORDER_LINE' 

爲什麼會出現呢?

回答

3

SQL中的賦值運算符是=而不是:=(僅適用於PL/SQL)。此外,該比較運營商也=而不是:=

所以這種說法:

update product_inventory 
    set qty_on_hand:= qty_on_hand - :new.quantity 
where product_id:= :new.product_id; 

應該

update product_inventory 
    set qty_on_hand = qty_on_hand - :new.quantity 
where product_id = :new.product_id; 
+0

謝謝觸發工作正常,但是當我試圖測試它給我的錯誤。我已經更新了帖子 – WT86 2014-09-26 13:23:44

+1

@ WT86觸發:要創建一個使用這條線交叉聯接:'從product_inventory,order_lines'從select語句中刪除'order_lines'表。這不是必需的。並確保'select'只返回單行(該錯誤消息所示) – 2014-09-26 13:26:20

+0

@ WT86:和它通常不是一個完全新的錯誤來改變這個問題是個好主意。你還是創建該問題的一個新的問題(原來的問題已經解決) – 2014-09-26 13:28:32

0

,您的觸發是不是多用戶的安全。由於用戶可以從表中選擇,並沒有看到其他用戶提交的修改,他們可以相信,有足夠的庫存,而事實上,當其他用戶提交他們的變化,也不會有。這可能導致qty_on_hand變爲負值。您需要進行檢查並更新原子過程。

此外,您沒有在更新中正確處理更改中的數量更改,也沒有考慮如果刪除order_lines會發生什麼情況(儘管可能有其他驗證會阻止此操作),因此數量會被刪除。

一種選擇將是SELECT...FOR UPDATE以防止其他用戶連看當前共有直到該用戶已完成並提交/回滾任何更改例如

CREATE OR REPLACE TRIGGER check_order_line 
    BEFORE INSERT OR UPDATE OR DELETE ON order_lines 
    FOR EACH ROW 
DECLARE 
    l_quantity_change product_inventory.qty_on_hand%TYPE; 
    l_current_stock product_inventory.qty_on_hand%TYPE; 
BEGIN 
    IF INSERTING THEN 
    l_quantity_change := :new.quantity; 
    ELSIF UPDATING THEN 
    l_quantity_change := 
     CASE 
     WHEN :new.quantity IS NOT NULL AND :old.quantity IS NOT NULL THEN 
      :new.quantity - :old.quantity 
     WHEN :new.quantity IS NOT NULL THEN 
      :new.quantity 
     WHEN :old.quantity IS NOT NULL THEN 
      -:old.quantity 
     ELSE 
      NULL 
     END; 
    ELSIF DELETING THEN 
    l_quantity_change := -:old.quantity; 
    END IF; 
    IF l_quantity_change IS NOT NULL THEN 
    SELECT qty_on_hand 
     INTO l_current_stock 
     FROM product_inventory 
    WHERE product_id = :new.product_id 
     FOR UPDATE OF qty_on_hand; 
    IF l_current_stock IS NOT NULL THEN 
     IF l_quantity_change > l_current_stock THEN 
     raise_application_error(-20103, 'Insufficient Stock'); 
     ELSE 
     UPDATE product_inventory 
      SET qty_on_hand = qty_on_hand - l_quantity_change 
     WHERE product_id = :new.product_id; 
     END IF; 
    END IF; 
    END IF; 
END; 

另一種選擇將是一個檢查約束添加到product_inventory表,以確保qty_on_hand不能爲負數。如果該值小於零,則會導致檢查約束違規錯誤,例如,

ALTER TABLE product_inventory 
    ADD CONSTRAINT check_qty_on_hand 
    CHECK (qty_on_hand IS NULL OR qty_on_hand >= 0) 

CREATE OR REPLACE TRIGGER check_order_line 
    BEFORE INSERT OR UPDATE OR DELETE ON order_lines 
    FOR EACH ROW 
DECLARE 
    l_quantity_change product_inventory.qty_on_hand%TYPE; 
BEGIN 
    IF INSERTING THEN 
    l_quantity_change := :new.quantity; 
    ELSIF UPDATING THEN 
    l_quantity_change := 
     CASE 
     WHEN :new.quantity IS NOT NULL AND :old.quantity IS NOT NULL THEN 
      :new.quantity - :old.quantity 
     WHEN :new.quantity IS NOT NULL THEN 
      :new.quantity 
     WHEN :old.quantity IS NOT NULL THEN 
      -:old.quantity 
     ELSE 
      NULL 
     END; 
    ELSIF DELETING THEN 
    l_quantity_change := -:old.quantity; 
    END IF; 
    IF l_quantity_change IS NOT NULL THEN 
    UPDATE product_inventory 
     SET qty_on_hand = qty_on_hand - l_quantity_change 
    WHERE product_id = :new.product_id 
     AND qty_on_hand IS NOT NULL; 
    END IF; 
END; 
+0

非常感謝你,但我不需要它那麼具體,我的觸發器我要求的是什麼? – WT86 2014-09-26 16:10:24

+0

如果您希望觸發器**保證**,如果沒有足夠的庫存來滿足訂單,則不能放置訂單;如果有足夠的庫存來滿足它,可以放置/修改訂單那麼你的觸發器不符合這些要求,訂單可能被放置在同一時間,可能被單獨允許進行,但是當完成時會導致負面的'qty_on_hand'。 – DrabJay 2014-09-26 16:19:17