计算产品库存的加权平均成本
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算产品库存的加权平均成本相关的知识,希望对你有一定的参考价值。
我必须计算我的产品库存成本,因此对于每次购买后的每个产品,我必须重新计算Weighted Average Cost。
我有一个观点,即每次进/出后都会给我当前产品的库存:
document_type document_date product_id qty_out qty_in price row_num stock_balance
SI 01/01/2014 52 0 600 1037.28 1 600
SI 01/01/2014 53 0 300 1357.38 2 300
LC 03/02/2014 53 100 0 1354.16 3 200
LC 03/02/2014 53 150 0 1355.25 4 50
LC 03/02/2014 52 100 0 1035.26 5 500
LC 03/02/2014 52 200 0 1035.04 6 300
LF 03/02/2014 53 0 1040 1356.44 7 1090
LF 03/02/2014 52 0 1560 1045 8 1860
LC 04/02/2014 52 120 0 1039.08 9 1740
LC 04/02/2014 53 100 0 1358.95 10 990
LF 04/02/2014 52 0 600 1038.71 11 2340
LF 04/02/2014 53 0 1040 1363.3 12 2030
LC 05/02/2014 52 100 0 1037.78 13 2240
LF 15/03/2014 53 0 20 1365.87 14 2050
LF 15/03/2014 52 0 50 1054.19 15 2290
我想添加一个计算的WAC
字段,如上所示:
document_type document_date product_id qty_out qty_in price row_num stock_balance WAC
SI 01/01/2014 52 0 600 1 037,28 1 600 1037,28000000000
SI 01/01/2014 53 0 300 1 357,38 2 300 1357,38000000000
LC 03/02/2014 53 100 0 1 354,16 3 200 1357,38000000000
LC 03/02/2014 53 150 0 1 355,25 4 50 1357,38000000000
LC 03/02/2014 52 100 0 1 035,26 5 500 1037,28000000000
LC 03/02/2014 52 200 0 1 035,04 6 300 1037,28000000000
LF 03/02/2014 53 0 1040 1 356,44 7 1090 1356,48311926606 --((1357,38*50)+(1040*1356,44))/(1090)
LF 03/02/2014 52 0 1560 1 045,00 8 1860 1043,75483870968 --((1037,28*300)+(1560*1045))/(1860)
LC 04/02/2014 52 120 0 1 039,08 9 1740 1043,75483870968
LC 04/02/2014 53 100 0 1 358,95 10 990 1356,48311926606
LF 04/02/2014 52 0 600 1 038,71 11 2340 1042,46129032258 --((1043,75483870968*1740)+(600*1038,71))/(2340)
LF 04/02/2014 53 0 1040 1 363,30 12 2030 1359,97000000000 --((1356,48311926606*990)+(1040*1363,3))/(2030)
LC 05/02/2014 52 100 0 1 037,78 13 2240 1042,46129032258
LF 15/03/2014 53 0 20 1 365,87 14 2050 1360,03301857239 --((1359,97551136621*2030)+(20*1365,87))/2050
LF 15/03/2014 52 0 50 1 054,19 15 2290 1042.71737568672 --((1042.46129032258*2240)+(50*1054.19))/2290
每种产品只有一种,只有一种文件类型'SI'
(初始库存),与之相关的价格是initial WAC
。
这是一个SQL Fiddle样本。
如果有人可以帮忙解决这个问题,我无法理解。
编辑:我已经通过在小数点后显示9个数字来提高精度来更新计算出的数字。
你需要使用递归CTE:
with recursive
stock_temp as (
select
*,
row_number() over(partition by product_id order by row_num) as rn
from
stock_table
)
,cte as (
select
document_type, document_date,
product_id, qty_out, qty_in, price,
row_num, stock_balance, rn,
price as wac
from
stock_temp where document_type = 'SI'
union all
select
sub.document_type, sub.document_date,
sub.product_id, sub.qty_out, sub.qty_in, sub.price,
sub.row_num, sub.stock_balance, sub.rn,
case when sub.qty_in = 0 then main.wac else
((sub.stock_balance - sub.qty_in) * main.wac + sub.qty_in * sub.price)
/ ((sub.stock_balance - sub.qty_in) + sub.qty_in) end as wac
from
cte as main
join stock_temp as sub
on (main.product_id = sub.product_id and main.rn + 1 = sub.rn)
)
select * from cte
我在这个移动平均线上花了几个小时!主要是因为不可靠的窗口函数first / last / nth_value,根据Postgresql documentation on window functions:This is likely to give unhelpful results for nth_value and particularly last_value.
答案是不完整的。要做的事:
- 当qty_in = 0时,使WAC保持相同的值(现在它被清零)
- WAC不是根据以前的WAC计算的,而是根据“之前的”价格计算的,这就是为什么我计算的WAC与您提供的WAC之间的差异越来越大
在这里我使用函数做了什么:
CREATE TYPE stock_table_with_wac AS
(document_type character varying,
document_date date,
product_id bigint,
qty_out double precision,
qty_in double precision,
price double precision,
row_num bigint,
stock_balance double precision,
wac double precision);
CREATE OR REPLACE FUNCTION calculate_wac_value()
RETURNS SETOF stock_table_with_wac AS
$BODY$
DECLARE
r_article stock_table_with_wac%rowtype;--maintain the liste of all products with there wac's value
r_in_out_article stock_table_with_wac%rowtype;--Each other records
BEGIN
--For each products
FOR r_article IN SELECT *, price FROM stock_table where document_type='SI' order by row_num
LOOP
return next r_article;
FOR r_in_out_article IN SELECT * FROM stock_table where document_type<>'SI' and product_id=r_article.product_id order by row_num
LOOP
--If there is an entry calculate the wac
if r_in_out_article.qty_in >0 then
r_in_out_article.wac:=((r_article.price * (r_in_out_article.stock_balance - r_in_out_article.qty_in)) + (r_in_out_article.qty_in * r_in_out_article.price))/(r_in_out_article.stock_balance);
--Update the wac value of the product
r_article.price:= r_in_out_article.wac;
else --The waca value still inchanged:
r_in_out_article.wac:= r_article.price;
end if;
RETURN NEXT r_in_out_article; -- return current row with caluculated wac if any
END LOOP;
return next r_article;
END LOOP;
RETURN;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION calculate_wac_value()
OWNER TO postgres;
select * from calculate_wac_value();
它似乎有一个正确的输出。这样处理是个好主意吗?
C中已经有一个可用于PostgreSQL的聚合函数,它的计算速度可能比SQL中的任何解决方案快得多:
https://github.com/Kozea/weighted_mean
我使用了以下文章中提供的用于FIFO逻辑的数据。
在这里,考虑了回报,但我以平均价格收取它们。
这是表结构:
ID ArticleID TranDate TranCode Items Price WACPrice WACRunningTotal
1 11782 2009-01-01 00:00:41.000 IN 809 256.82 256.82 NULL
2 16967 2009-01-01 00:00:50.000 IN 372 134.44 134.44 NULL
3 13078 2009-01-01 00:01:21.000 IN 532 201.69 201.69 NULL
4 10918 2009-01-01 00:01:34.000 IN 717 348.79 348.79 NULL
5 18871 2009-01-01 00:01:34.000 IN 1045 88.25 88.25 NULL
6 22379 2009-01-01 00:03:01.000 IN 401 326.59 326.59 NULL
7 24049 2009-01-01 00:03:24.000 IN 222 54.54 54.54 NULL
8 12570 2009-01-01 00:03:33.000 IN 731 29.25 29.25 NULL
9 10327 2009-01-01 00:03:33.000 IN 407 222.69 222.69 NULL
10 21548 2009-01-01 00:03:49.000 IN 400 254.05 254.05 NULL
11 15155 2009-01-01 00:03:51.000 IN 719 320.02 320.02 NULL
12 22706 2009-01-01 00:04:00.000 IN 331 25.91 25.91 NULL
13 19126 2009-01-01 00:04:16.000 IN 289 305.47 305.47 NULL
14 21722 2009-01-01 00:04:39.000 IN 434 3.80 3.80 NULL
15 20811 2009-01-01 00:05:57.000 IN 1043 316.57 316.57 NULL
16 21998 2009-01-01 00:06:01.000 IN 1009 15.18 15.18 NULL
17 12928 2009-01-01 00:06:45.000 IN 1122 265.71 265.71 NULL
18 14150 2009-01-01 00:07:36.000 IN 730 148.91 148.91 NULL
19 22307 2009-01-01 00:08:09.000 IN 986 184.38 184.38 NULL
20 17472 2009-01-01 00:08:34.000 IN 1182 62.73 62.73 NULL
请按照上述帖子中的ddl语句进行操作。
我使用过Cte,但是对于1000001条记录,它最大化了maxrecursion计数。所以我创建了一个由一个项id执行的过程,该过程可以由另一个过程迭代。
我在库存表中添加了两列,WACPrice和WACRunningTotal。
请在下面找到我的代码:
alter proc sp_GetInventoryDetails_ByWAC
-- sp_GetInventoryDetails_ByWAC 10017
@ArticleId int
as
begin
-- select * from #stocktemp order by trandate
--select *, 0 as WACRunningTotal into #StockTemp from stock where articleid=10000 order by TranDate;
;WITH y AS
(
SELECT articleid,TranDate,trancode, items, rn = ROW_NUMBER() OVER (ORDER BY TranDate)
FROM stock where ArticleID =@ArticleId
), x AS
(
SELECT articleid, TranDate,trancode, rn, items, rt = items
FROM y
WHERE rn = 1
UNION ALL
SELECT y.articleid,y.TranDate,y.trancode, y.rn, y.items,case when y.TranCode='OUT' then x.rt - y.items else x.rt+y.Items end
FROM x INNER JOIN y
ON y.rn = x.rn + 1
)
update st set st.WACRunningTotal=x.rt
from stock st
inner join x on x.ArticleID=st.ArticleID and x.TranDate=st.TranDate and x.TranCode=st.TranCode and isnull(st.WACRunningTotal,0)=0
OPTION (MAXRECURSION 0);
;WITH StockCTE
AS (SELECT articleid,
items,
WACRunningTotal,
WACPrice,
trandate,
ROW_NUMBER() OVER (PARTITION BY articleid ORDER BY trandate) RowNum
FROM stock where ArticleID =@ArticleId),
/* CleanStock - A recursive CTE. This runs down the list of values for each stock, checking the Price column, if it is null it gets the previous non NULL value.*/
CleanStock
AS (SELECT articleid,
items,
WACRunningTotal,
ISNULL(WACPrice ,0) WACPrice ,/* Ensure we start with no NULL values for each stock */
trandate,
RowNum
FROM StockCTE cur
WHERE RowNum = 1
UNION ALL
SELECT Curr.articleid,
curr.items,
Curr.WACRunningTotal,
case when Curr.WACPrice=0 then prev.WACPrice else ((curr.WACPrice*curr.Items)+(prev.WACRunningTotal*prev.WACPrice))/curr.WACRunningTotal end as WACPrice,
Curr.trandate,
以上是关于计算产品库存的加权平均成本的主要内容,如果未能解决你的问题,请参考以下文章