我正在做一个小项目,复制一个CVS药房数据库。了解PL / SQL中的触发器,我认为创建一个触发器是一个好主意,该触发器会在商店(交易)中购买该产品时更新商店的产品库存。
现在为了这样做,我必须在productLine_T上进行AFTER INSERT(使用数量购买交易中的产品的表),存储transactionID,找到新插入的交易的employeeID,获取storeID来自该交易的员工,然后允许我通过storeID获取相应的库存,以通过productLine_T中的orderedQuantity更新该商店库存中的productID。
创建此触发器后,当我尝试插入productLine_T时出现以下错误,如上所述:
Error starting at line 193 in command:
INSERT INTO productLine_T(transactionID, productID, productQuantity, productFinalPrice)
VALUES(00000006, 00000001, 2, 1.50)
Error report:
SQL Error: ORA-04091: table BSEWARDS.PRODUCTLINE_T is mutating, trigger/function may not see it
ORA-06512: at "BSEWARDS.ONPRODUCTPURCHASED", line 10
ORA-04088: error during execution of trigger 'BSEWARDS.ONPRODUCTPURCHASED'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
我不明白为什么触发器无法在插入新值后读取?我可以在请求时提供我的ERD和任何其他可视化表示。
* 触发:*
create or replace
trigger onProductPurchased
AFTER INSERT on productLine_T
for each row
declare
transQty productLine_T.productQuantity%type;
transID productLine_T.transactionID%type;
prodID productLine_T.productID%type;
empID Employee_T.employeeID%type;
stID Store_T.storeID%type;
--outOfStock exception;
begin
--store new transactionID in transID for query
select transactionID, productID, productQuantity into transID, prodID, transQty
FROM productLine_T
WHERE productLine_T.transactionID = :new.transactionID;
--find store by employee of transactionID to get inventory of store
-- get employeeID of newly inserted transaction
select emp.employeeID into empID FROM Employee_T emp, Transaction_T trans
WHERE trans.transactionID = transID;
-- get storeID from employee at store who made the transaction
select st.storeID into stID FROM Store_T st, Employee_T emp
WHERE emp.employeeID = empID;
--get inventory by storeID and change inventoryQty based on productID quantity
update Inventory_T
set inventoryQuantity = inventoryQuantity - :new.productQuantity
WHERE Inventory_T.storeID = stID AND Inventory_T.storeID = prodID;
end;
答案 0 :(得分:2)
PL / SQL中的行级触发器不能SELECT
来自它们所放置的表而不会导致mutating table错误。在你的情况下,我认为你甚至不需要SELECT
- 你不能只是替换它:
SELECT transactionID, productID, productQuantity into transID, prodID, transQty
FROM productLine_T
WHERE productLine_T.transactionID = :new.transactionID;
用这个:
transID := :new.transactionID;
prodID := :new.productID;
transQty := :new.productQuantity;
您遇到的第二个问题是触发器中的两个查询都引用了多个表,但您没有指定任何连接条件,这意味着您将获得两个表中的cartesian product。你实际上并不关心员工,你只关心商店,所以你最好用一个只能取出你需要的查询来替换这两个查询:
SELECT st.storeID INTO stID
FROM Transaction_T trans
INNER JOIN Employee_T emp ON (emp.employeeID = trans.employeeID)
INNER JOIN Store_T st ON (st.storeID = emp.storeID)
WHERE trans.transactionID = transID;
我在这里对你的架构做了一些猜测,但它可能足以让你到达那里。请注意使用ANSI连接语法,它巧妙地将连接条件(在ON
子句中)与过滤谓词(在WHERE
子句中)分开。
值得注意的是,有些人认为使用SELECT ... INTO ...
语句是不好的形式,因为如果你有零行或多行,它将引发异常。最好将查询放入游标,然后从中获取一次。通过这种方式,您知道只会获得一条记录,并且可以通过检查%NOTFOUND
光标属性轻松防止零记录。