PL / SQL程序将逐个浏览库存,查找项目并相应更新

问题描述 投票:2回答:4

我有一个如下的库存表:

warehouse_no|item_no|item_quantity
------------|-------|-------------
 1          | 1000  |          300
------------|-------|-------------
 2          | 1000  |          500
------------|-------|-------------
 3          | 1000  |          200
------------|-------|-------------
 1          | 2000  |          100
------------|-------|-------------
 2          | 2000  |          200
------------|-------|-------------
 3          | 2000  |            0
------------|-------|-------------
 1          | 3000  |          100
------------|-------|-------------
 2          | 3000  |          200
------------|-------|-------------
 3          | 3000  |            0
------------|-------|-------------

现在,如果有人例如订购400单位项目没有。 1000,pl / sql应该通过表,从仓库1取300,从仓库2取100并更新它。结果表应如下所示:

warehouse_no|item_no|item_quantity
------------|-------|-------------
 1          | 1000  |            0
------------|-------|-------------
 2          | 1000  |          400
------------|-------|-------------
 3          | 1000  |          200
------------|-------|-------------

我写的程序如下

PROCEDURE upd_inventory(p_item_no inventory.item_no%TYPE, p_quantity number) 
AS
 CURSOR inventory_cur IS
  select MAX(item_quantity)as quantity
  from   inventory
  where  item_no=p_item_no;

  v_order_quantity number:=p_quantity;

BEGIN
 FOR  v_inventory_cur IN inventory_cur LOOP
  UPDATE INVENTORY
  SET item_quantity = ((v_inventory_cur.quantity) - p_quantity );
  COMMIT;
 END LOOP;
END upd_inventory;

但正如您将注意到的那样,这将更新整个数量列,并且无法解决逐行检查项目并相应更新的问题。

谢谢

oracle loops stored-procedures plsql
4个回答
1
投票

可以使用一个SQL查询。以下查询显示了如何计算从每个仓库中获取的数量,并返回一个仓库列表,其中我们总共获得400个单位的项目号。 1000:

select warehouse_no, item_no, item_quantity,
       case when running_sum < 400 then item_quantity
            else 400 - (running_sum - item_quantity) end how_much_to_take
  from (select warehouse_no, item_no, item_quantity, 
               sum(item_quantity) over (partition by item_no 
                                        order by warehouse_no) running_sum
          from inventory
         where item_no = 1000)
 where running_sum - item_quantity < 400

how_much_to_take包含数量,我们需要从仓库中取出多少物品。

所以我们可以写下面的MERGE声明:

merge into inventory i
using ( select warehouse_no, item_no, item_quantity, running_sum,
               case when running_sum < 400 then item_quantity
                    else 400 - (running_sum - item_quantity) end to_take
          from (select warehouse_no, item_no, item_quantity, 
                       sum(item_quantity) over (partition by item_no 
                                                order by warehouse_no) running_sum
                  from inventory
                 where item_no = 1000)
         where running_sum - item_quantity < 400
        ) how_much
   on (i.warehouse_no = how_much.awrehouse_no and i.item_no = how_much.item_no)
 when matched then update
  set i.item_quntity = i.item_quntity - how_much.to_take

此语句将根据需要更新您的表。而且,如果您仍需要一个程序:

PROCEDURE upd_inventory(p_item_no inventory.item_no%TYPE, p_quantity number) AS
BEGIN
    merge into inventory i
    using ( select warehouse_no, item_no, item_quantity, running_sum,
                   case when running_sum < p_quantity then item_quantity
                        else p_quantity - (running_sum - item_quantity) end to_take
              from (select warehouse_no, item_no, item_quantity, 
                           sum(item_quantity) over (partition by item_no 
                                                    order by warehouse_no) running_sum
                      from inventory
                     where item_no = p_item_no)
             where running_sum - item_quantity < p_quantity
            ) how_much
       on (i.warehouse_no = how_much.awrehouse_no and i.item_no = how_much.item_no)
     when matched then update
      set i.item_quntity = i.item_quntity - how_much.to_take;
END upd_inventory;

0
投票

使用item_no和warehouse_no作为条件来更新库存表中的item_quantity。在其中您需要找到一个模式以在where条件中使用warehouse_no。


0
投票

以下功能适用于您的要求:

CREATE OR REPLACE PROCEDURE 
DEDUCT_INV (in_item_no In Number,in_quantity  In Number) 
Is

TYPE someRefCursor IS REF CURSOR;
fetchWareHouseQuantitiesCursor someRefCursor;

tempWareHouseNo temp_inventory.warehouse_no%type;
tempItemQuantity temp_inventory.item_quantity%type;
requiredQuantity temp_inventory.item_quantity%type := in_quantity;

Begin

    Open fetchWareHouseQuantitiesCursor For 
    Select warehouse_no, item_quantity 
    From temp_inventory 
    Where item_no = in_item_no And item_quantity != 0
    order by warehouse_no;

    /* Ignoring 0 quantity warehouses 
    & also ordering by warehouse 
    but if required can be ordered by item_quantity desc so
    minimum warehouses are touched which is more efficient*/

    LOOP
        Fetch fetchWareHouseQuantitiesCursor Into tempWareHouseNo,tempItemQuantity;

        Dbms_Output.Put_Line('Required:'||requiredQuantity||'.');

        Exit When fetchWareHouseQuantitiesCursor%NotFound;

        Dbms_Output.Put_Line('Fetched:'||tempWareHouseNo||','||tempItemQuantity||'.');

        if(requiredQuantity > tempItemQuantity)
        then

            Dbms_Output.Put_Line('Updating:'||tempWareHouseNo||','||tempItemQuantity||' by 0.');

            update temp_inventory set item_quantity = 0 where warehouse_no = tempWareHouseNo And item_no = in_item_no;

            requiredQuantity:= requiredQuantity - tempItemQuantity;

        else

            Dbms_Output.Put_Line('Updating:'||tempWareHouseNo||','||tempItemQuantity||' by '||(tempItemQuantity - requiredQuantity)||'.');

            update temp_inventory set item_quantity = item_quantity-requiredQuantity where warehouse_no = tempWareHouseNo And item_no = in_item_no;

            requiredQuantity:= 0;

            exit;

        end if;

    END LOOP;

    Close fetchWareHouseQuantitiesCursor;

    if(requiredQuantity != 0)
    then
        rollback;

        Dbms_Output.Put_Line('Job Failed. Insufficient storage. Missing:'||requiredQuantity||' quantity.');
    else
        commit;

        Dbms_Output.Put_Line('Job Completed Successfully.');
    end if;

    return;

End;
/

execute DEDUCT_INV(1000,400);

0
投票

其他人已经回答了这个问题,但为了好玩,我会分享我开始的版本:

procedure upd_inventory
    ( p_item_no   inventory.item_no%type
    , p_quantity  inventory.item_quantity%type )
is
    l_total_stocked inventory.item_quantity%type := 0;
    l_outstanding inventory.item_quantity%type := p_quantity;
begin
    dbms_output.put_line('Item ' || p_item_no || ' target: ' || p_quantity);

    for r in (
        select i.item_no, i.warehouse_no
             , i.item_quantity as warehouse_quantity
             , i.item_quantity as warehouse_remaining
             , sum(i.item_quantity) over () as total_quantity
        from   inventory i
        where  i.item_no = p_item_no
        and    i.item_quantity > 0
        for update
        order by i.item_quantity desc
    )
    loop
        l_total_stocked := r.total_quantity;  -- redundant after first iteration but avoids separate query

        update inventory i
        set    i.item_quantity = greatest(r.warehouse_quantity - l_outstanding, 0)
        where  i.warehouse_no = r.warehouse_no
        and    i.item_no = p_item_no
        returning i.item_quantity into r.warehouse_remaining;

        dbms_output.put_line('Warehouse '||r.warehouse_no || ': reducing stock by ' || (r.warehouse_quantity - r.warehouse_remaining));

        l_outstanding := l_outstanding - (r.warehouse_quantity - r.warehouse_remaining);

        exit when l_outstanding = 0;
    end loop;

    if l_outstanding = 0 then
        dbms_output.put_line('Item ' || p_item_no || ' stock reduced by ' || p_quantity);
    else
        raise_application_error
        ( -20000
        , 'Insufficient stock for item ' || p_item_no ||
          ': stocked: ' || l_total_stocked || ', requested: ' || p_quantity ||
          ', short: ' || (p_quantity - l_total_stocked) );
    end if;

end upd_inventory;

虽然单个merge方法无疑是最快的(并且避免了在多用户环境中防止丢失更新所需的锁定),但我个人会担心支持和维护。也许我可以使用model子句或递归with重写我的光标,但是可怜的开发人员有一天要更新它以考虑交付距离或某事将会有更难的工作。

我没有在程序中提交或回滚,因为通常最好将它留给调用者,但是如果你愿意,你可以在最后提交(不在中间!)注意,引发异常会隐式回滚更新。如果需要显式回滚,则在开始时声明一个保存点并回滚到该保存点。

可能真正的程序会使用标准记录器来诊断消息而不是dbms_output

© www.soinside.com 2019 - 2024. All rights reserved.