我有一个如下的库存表:
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;
但正如您将注意到的那样,这将更新整个数量列,并且无法解决逐行检查项目并相应更新的问题。
谢谢
可以使用一个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;
使用item_no和warehouse_no作为条件来更新库存表中的item_quantity。在其中您需要找到一个模式以在where条件中使用warehouse_no。
以下功能适用于您的要求:
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);
其他人已经回答了这个问题,但为了好玩,我会分享我开始的版本:
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
。