0

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

1 回答 1

2

PL/SQL 中的行级触发器不能SELECT从它们所在的表中提取而不会导致变异表错误。在你的情况下,我认为你甚至不需要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;

您遇到的第二个问题是触发器中的两个查询都引用了多个表,但您没有指定任何连接条件,这意味着您将获得两个表的笛卡尔积。您实际上并不关心员工,您只关心商店,因此您最好将两个查询替换为一个仅获取您需要的查询:

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

于 2013-04-15T18:24:59.013 回答