PL / SQL触发器无法读取表AFTER INSERT

时间:2013-04-15 16:51:46

标签: sql database oracle plsql triggers

我正在做一个小项目,复制一个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;

1 个答案:

答案 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光标属性轻松防止零记录。