Oracle數據分攤問題解析

經常會碰到,由于業務需要,需要將某種匯總的數據按照一定的原則分攤給一堆數據。 其實,如果邏輯清晰的話,這類型的程序還是比較好些的。 本文重點是如果用簡單的程序實現這種效果,而且不容易分攤分錯。 所有的分攤問題,首先必須要搞清楚以下幾點問題: 1

經常會碰到,由于業務需要,需要將某種匯總的數據按照一定的原則分攤給一堆數據。
其實,如果邏輯清晰的話,這類型的程序還是比較好些的。
本文重點是如果用簡單的程序實現這種效果,而且不容易分攤分錯。

所有的分攤問題,首先必須要搞清楚以下幾點問題:
1 首要的,要確定 什么東西,多少數量 分攤給什么東西?舉個形象的例子,一桶沙子分攤給一些瓶子。
2 分攤的先后原則。上面的例子,一桶沙子分攤給一些瓶子,那瓶子的被分攤順序是什么樣子的?沙子先給哪個瓶子?要先確定清楚。

說得好像有點麻煩,舉個例子說明。
最近接到的一個需求:
PO入庫的時候,批次可能重復輸入,所以入庫之后,庫存已經匯總在一起了。然后用戶對(匯總的)庫存進行消耗(就是雜發)。
現在需要有個報表可以知道:按照先進先出的原則,區分用戶的一段期間內的消耗數量 對應的是那筆入庫單號。

備注:假設下面的數量對應都是主單位。

7.1 入庫單R1? 料號A 批次P1 接收入庫 400
7.3 入庫單R2? 料號A 批次P1 接收入庫 300
這時候,P1批次庫存共 700
——-消耗(雜發)明細
7.10 消耗P1 100
7.12 消耗P1 200
8.10 消耗P1 200
8.13 消耗P1 100
8.20 消耗P1 50
9.20 消耗P1 50

如果查詢報表的日期選擇的是:8.1~8.31
???? 8.1號 之前共消耗100+200=300
8.1~8.31號 之內一共消耗:200+100+50=350

所以核心問題是要將350如何分攤在R1和R2里面。
要實現的分攤效果:
????? 入庫總數????? 之前消耗的分攤??? 期間內消耗的分攤
R1?????? 400?????????? 300????????????????? 100
R2?????? 300??????????? 0?????????????????? 250

所以,結果是,報表是:8.1~8.31
一共消耗350,對應入庫單的消耗情況:
R1消耗100
R2消耗250

實現邏輯:
你可以假想,現在有2個沙桶,
紅色的沙桶裝的沙子是 之前消耗的分攤 的數量
黑色的沙桶裝的沙子是 期間內消耗的分攤 的數量
每張入庫單就是一個瓶子,所以共有2個瓶子,R1和R2。現在是如何將 紅色的沙子 和 黑色的沙子 裝到這2個瓶子里面。

裝沙規則:
1 用沙子的順序:先用 紅色的沙子,用完之后,再用黑色的沙子。
2 裝瓶子的順序:按照先進先出的原則,必須先裝瓶子R1,再裝R2.

DECLARE
? L_PRE_PERIOD_QTY NUMBER; —期間前的匯總消耗量 —之前消耗的分攤 的數量—紅色的沙子
? L_CURR_PERIOD_QTY NUMBER ; –本期的匯總消耗量–期間內消耗的分攤 的數量—黑色的沙子
? —-裝的結果用記錄類型存下來,因為后面要用到。
?? TYPE shipment_consume_Rec_Type?? IS RECORD
?? (
???? SHIPMENT_LINE_ID? NUMBER
?? , PRIMARY_QUANTITY???????????? NUMBER
?? , consume_pre_qty NUMBER
?? , consume_curr_qty NUMBER
????? );
??? TYPE shipment_consume_Tbl_Type IS TABLE OF shipment_consume_Rec_Type
??? INDEX BY BINARY_INTEGER ;
??? L_shipment_consume_Tbl shipment_consume_Tbl_Type;
??? N NUMBER;
BEGIN
? —-1 首先要算出紅色的沙子和黑色的沙子的總數量,就是有多少數量可分攤。
SELECT nvl(sum(case when MMT.transaction_date ???? ABS(NVL(MTLN.PRIMARY_QUANTITY,0))
???? else
?????? 0
???? end ),0) PRE_PERIOD_QTY,
???? nvl(sum(case when MMT.transaction_date >= :P_F_START_DATE then
???? ABS(NVL(MTLN.PRIMARY_QUANTITY,0))
???? else
?????? 0
???? end ),0) CURR_PERIOD_QTY
?? INTO L_PRE_PERIOD_QTY,L_CURR_PERIOD_QTY
? FROM MTL_MATERIAL_TRANSACTIONS MMT
????? ,MTL_TRANSACTION_LOT_NUMBERS MTLN
WHERE MMT.TRANSACTION_ID = MTLN.TRANSACTION_ID
?? AND MMT.TRANSACTION_TYPE_ID = 74
?? AND MMT.TRANSACTION_ACTION_ID = 6
?? AND MMT.OWNING_TP_TYPE = 1 —所有權轉出的(寄售供應商的庫存)
?? —
?? AND MMT.ORGANIZATION_ID = 103
?? AND MMT.INVENTORY_ITEM_ID = 11783561
?? AND MTLN.LOT_NUMBER = ‘P0000001’
?? AND MMT.transaction_date ?? DBMS_OUTPUT.PUT_LINE(‘L_PRE_PERIOD_QTY:’||L_PRE_PERIOD_QTY||’ -L_CURR_PERIOD_QTY:’||L_CURR_PERIOD_QTY);
?? N := 1;

?? —–2 分攤主邏輯。
?? FOR REC_SHIPMENT_LINE IN (
?????? —–瓶子(入庫單)的游標
??? SELECT MMT.ORGANIZATION_ID
??????? ,MMT.INVENTORY_ITEM_ID
??????? ,MTLN.LOT_NUMBER
??????? ,MTLN.TRANSACTION_DATE
??????? ,RT.SHIPMENT_HEADER_ID
??????? ,RT.SHIPMENT_LINE_ID
??????? ,MTLN.PRIMARY_QUANTITY
??? FROM MTL_TRANSACTION_LOT_NUMBERS MTLN, MTL_MATERIAL_TRANSACTIONS MMT,RCV_TRANSACTIONS RT
?? WHERE MTLN.TRANSACTION_ID = MMT.TRANSACTION_ID
???? AND RT.TRANSACTION_ID = MMT.RCV_TRANSACTION_ID
???? AND MMT.TRANSACTION_TYPE_ID = 18
???? AND MMT.TRANSACTION_SOURCE_TYPE_ID = 1
???? AND XYG_PO_PKG.CHECK_PO_LINE_CONSIGN(RT.PO_LINE_ID) = ‘Y’
???? AND MMT.ORGANIZATION_ID = 103
???? AND MMT.INVENTORY_ITEM_ID = 11783561
???? AND MTLN.LOT_NUMBER = ‘P0000001’
???? ORDER BY MTLN.TRANSACTION_DATE,MMT.TRANSACTION_ID) LOOP
?????? —2.1 優先消耗期初之前的耗料數量,就是先用紅色的沙子的數量。
?????? IF L_PRE_PERIOD_QTY >= REC_SHIPMENT_LINE.PRIMARY_QUANTITY THEN —當紅色沙子的數量大于瓶子的容量的時候。
???????? L_shipment_consume_Tbl(N).SHIPMENT_LINE_ID :=REC_SHIPMENT_LINE.SHIPMENT_LINE_ID;
???????? L_shipment_consume_Tbl(N).PRIMARY_QUANTITY :=REC_SHIPMENT_LINE.PRIMARY_QUANTITY;
???????? —-消耗紅沙的數量就是瓶子的容量。
???????? L_shipment_consume_Tbl(N).consume_pre_qty :=REC_SHIPMENT_LINE.PRIMARY_QUANTITY;
???????? L_shipment_consume_Tbl(N).consume_curr_qty :=0;
???????? —-期初數量就是剩下要分配的數量。因為紅色沙子已經被消耗掉一部分了。
???????? L_PRE_PERIOD_QTY :=L_PRE_PERIOD_QTY-REC_SHIPMENT_LINE.PRIMARY_QUANTITY;
?????? ELSE —當紅色沙子的數量小于瓶子容量的時候
???????? L_shipment_consume_Tbl(N).SHIPMENT_LINE_ID :=REC_SHIPMENT_LINE.SHIPMENT_LINE_ID;
???????? L_shipment_consume_Tbl(N).PRIMARY_QUANTITY :=REC_SHIPMENT_LINE.PRIMARY_QUANTITY;
???????? —-瓶子裝 紅色沙子的數量就是紅色沙子的數量了
???????? L_shipment_consume_Tbl(N).consume_pre_qty :=L_PRE_PERIOD_QTY;

???????? —-這時候已經用完紅色沙子了,開始用黑色沙子了—–
?????????? —2.2 當黑色沙子數量大于瓶子 可用的容量 的時候。
???????? IF L_CURR_PERIOD_QTY > (REC_SHIPMENT_LINE.PRIMARY_QUANTITY – L_PRE_PERIOD_QTY) THEN
?????????? —該瓶子 裝黑色沙子的數量 就是 瓶子的可用容量。
?????????? L_shipment_consume_Tbl(N).consume_curr_qty := REC_SHIPMENT_LINE.PRIMARY_QUANTITY – L_PRE_PERIOD_QTY;
?????????? —本次還有多少數量需要被下一個單號分攤,就是確定剩下還有多少黑色沙子可用。
?????????? L_CURR_PERIOD_QTY := L_CURR_PERIOD_QTY – L_shipment_consume_Tbl(N).consume_curr_qty;
???????? ELSE
?????????? —-當黑色沙子數量 小于或者等于 瓶子的可用容量的時候
?????????? —–該瓶子裝黑色沙子的數量就是 瓶子的可用容量。
?????????? L_shipment_consume_Tbl(N).consume_curr_qty := L_CURR_PERIOD_QTY;
?????????? —–黑色沙子用完咯!~~一定要賦值0,因為根據黑色沙子的使用情況判斷后面是否要退出瓶子的循環。
?????????? L_CURR_PERIOD_QTY := 0;
???????? END IF;
???????? L_PRE_PERIOD_QTY:= 0;
?????? END IF;
??????? N := N+1;
??????? —-當黑色沙子用完的時候,要退出循環。因為沙子數量可能很少,但是瓶子很多。。。沒必要再循環下去了。
??????? IF L_CURR_PERIOD_QTY ???????? EXIT;
??????? END IF;
???? END LOOP;
????
???? —顯示裝的結果。
???? FOR I IN 1..L_shipment_consume_Tbl.COUNT LOOP
????? DBMS_OUTPUT.PUT_LINE(L_shipment_consume_Tbl(I).SHIPMENT_LINE_ID
????? ||’-‘|| L_shipment_consume_Tbl(I).PRIMARY_QUANTITY
????? ||’-‘|| L_shipment_consume_Tbl(I).consume_pre_qty
????? ||’-‘|| L_shipment_consume_Tbl(I).consume_curr_qty
????? );
?????
???? END LOOP;
END;

/*
—例如:
L_PRE_PERIOD_QTY:0 -L_CURR_PERIOD_QTY:2020.2
18467366-1605.5-0-1605.5
18633076-5014.7-0-414.7

*/

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享