我有以下表格:
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'
为什么我会这样做?
答案 0 :(得分: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;
答案 1 :(得分: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;