Oracle Trigger从产品中删除库存数量并添加到订单行

时间:2011-12-06 11:02:51

标签: oracle join triggers

我提前为这个令人困惑的问题,文本墙和可怕的触发器道歉。我正在为零售商店设计一个小型数据库,该商店在店内接受订单,然后从现场仓库/库房中提供产品。

现在,订单实体与order_line实体之间存在一对多关系,而order_line实体又与产品(其本身存储在库存等中)具有多对一关系。 order_line实体是链接实体并解决了多对多关系,所以这一切都很好。只是为了澄清,这是每个产品的一个order_line。

我想要做的是当创建order_line(具有数量属性)时,我希望触发器首先检查相应的产品是否有足够的库存(因此如果数量为3,则库存必须至少为3),否则它必须抛出错误。

如果成功,我希望它相应地更新数量和库存属性。我还希望它为order_line添加一个小计值(我还没有尝试过),然后可以用来计算订单实体的总价值。

所以在这个阶段,我正在寻找一些指导,因为我现在对此非常困惑。

CREATE OR REPLACE TRIGGER check_order_line  
BEFORE INSERT OR UPDATE ON order_line 

for each row
BEGIN 
select order_line.quantity, products.stock from order_lines right join products on       order_line.product_no=products.product_no;
if(order_line.quantity>products.stock) then
RAISE_APPLICATION_ERROR(-20103, 'Insufficient Stock');
ELSE
products.stock := products.stock - quantity;
 dbms_output.put('Successful');
END IF;
END; 
. 
run

我遇到的错误:

2/1      PL/SQL: SQL Statement ignored
2/49     PL/SQL: ORA-00942: table or view does not exist
3/1      PL/SQL: Statement ignored
3/15     PLS-00357: Table,View Or Sequence reference 'ORDER_LINE.QUANTITY'
     not allowed in this context

我尝试了什么:

  • 我不确定前两个错误;有问题的表是 肯定叫做order_line,也许我错过了一些明显的东西。
  • 我也尝试为products.stock和order_line声明变量 数量来解决最后一个错误 - 这编译了IIRC,但没有 实际工作,因为我猜它不是更新表。
  • 我不太担心其他行动,我可能需要更新 表格声明,但现在我只专注于获得 触发条件工作。
  • 如果有人能指出我的话 方向,并指出任何我喜欢它的搞笑错误。

    非常感谢你的时间,我为这种怪异的触发器伤到你的眼睛而道歉。

2 个答案:

答案 0 :(得分:3)

对products.stock施加约束以强制执行该值> -1:

ALTER TABLE products add CONSTRAINT has_stock CHECK (stock >-1);

然后将updateinsert作为单个交易执行。

UPDATE product SET products.stock = products.stock - quantity_required
WHERE product_id=id_of_product

INSERT INTO order_line ...............

COMMIT;

如果库存不足,交易将始终失败,您将不会遇到与触发器相关的问题。

假设您此时没有股票价格,您可以使用RETURNING上的update条款获取股票价格(您需要声明变量v_product_cost以保留价值)例如:

UPDATE product SET products.stock = products.stock - quantity_required
WHERE product_id=id_of_product
RETURNING products.value INTO v_product_cost

然后,您可以在随后的插入中使用此值。

答案 1 :(得分:1)

  1. 最后的run命令没有意义。这是SQL Server语法。
  2. 在您的查询中,您引用的是表ORDER_LINES(复数)。但触发器是在表ORDER_LINE(单数)上定义的。我假设您没有ORDER_LINEORDER_LINES表,因此我希望您的查询可以引用ORDER_LINE表。
  3. 在表A上定义的行级触发器通常不能查询表A.因此,由于您的触发器是在ORDER_LINE上定义的,因此无法查询ORDER_LINE。看起来您真的只想要导致触发器触发的行的信息,因此您实际上不需要加入ORDER_LINE表。您只需要引用:NEW记录中的属性。
  4. PL / SQL中的SELECT语句需要对结果执行某些操作。据推测,您的目的是做一个SELECT ... INTO局部变量。
  5. 如果您要更新PRODUCTS表,则需要执行实际的UPDATE
  6. 把所有这些放在一起,我的猜测是你试图创建一个类似于

    的触发器
    CREATE OR REPLACE TRIGGER check_order_line  
      BEFORE INSERT OR UPDATE ON order_line 
      for each row
    DECLARE
      l_current_stock products.stock%type;
    BEGIN 
      select products.stock 
        into l_current_stock
        from products
       where product_no = :new.product_no;
      if(:new.quantity > l_current_stock) then
        RAISE_APPLICATION_ERROR(-20103, 'Insufficient Stock');
      else
        update products
           set stock := stock - :new.quantity
         where product_no := :new.product_no;
      end if;
    END;
    

    然而,所有这一切,触发器通常不是解决此类问题的正确方法。从维护的角度来看,如果没有别的东西,那么插入所有PROCESS_ORDER行并更新所有ORDER_LINE行的存储过程PRODUCTS将更容易遵循和调试。嵌入的业务逻辑越多,就越难以跟踪应用程序的流程,并且更容易最终得到一个几乎不可能放松的无意更新的老鼠窝。

    另外,请注意多用户系统中会发生什么。会话A可以查询PRODUCTS表并查看STOCK的5并接受该产品的4个单位的订单。但是在会话A发出提交之前,会话B也会查询PRODUCTS表的同一行,看到相同的STOCK为5,并接受3个单位的订单。会话B的UPDATE语句将阻塞,直到会话A提交。但是如果A提交和B提交,则两个订单都将被输入,PRODUCTS表将显示-2的STOCK。这就是为什么你需要凯文建议的CHECK约束

    ALTER TABLE products
      ADD CONSTRAINT chk_positive_stock CHECK( stock >= 0 );