库龄分析表,一般的进销存都要提供。大多数的库龄分析要求分析1个月内,1-2个月,2-3个月,3-6个月,6-12个月,1-2年,2-3年,3年以上。以下例子按照这个来设计。(月的概念其实不精确,一般按照30天或者31来计算月)
先进先出的库龄分析算法
先统计当前机构、仓库下,产品对应的库存数量。然后按照产品的入库历史去追溯,直到当前数量减去入库数量总数小于号这等于0,记录下来这个入库时间。然后把这些入库按照库龄分析要求的时间段进行归类即可。
举例说明一下:
假设产品A的库存数量是50个。假设当前时间是6月1号。
假设产品A的入库历史如下:
序号 产品 入库时间 入库数量
1 A 6月1日 20
2 A 5月3日 10
3 A 5月1日 15
4 A 3月20日 30
5 A 2月3日 25
......
按照我们设定的数据50,
减去序号1的20个,剩余30个,大于0,继续;
减去序号2的10个,剩余20个,大于0,继续;
减去序号3的15个,剩余5个,大于0,继续;
减去序号4的30个,结果为负数,OK,计算到这里截至。根据这个时间,我们知道,最早的入库时间是4月20日。
然后按照序号1、2、3、4去归类,序号1、2归类到1个月内,序号3归类到1-2个月,序号4归类到2-3个月;
库龄分析的结果是:
1个月内 20
1-2个月 25
2-3个月 5
其余为0
SQL例子
更进一步,我们可以考虑先把出入库历史按照时间段分好,然后统计好数量,直接用当前库存数据减去各个区间的数据,这样直接就得出结果。下面的实际例子就是按照这种方式来做的。
用存储过程直接统计,可能会存在性能上的问题。如果要考虑性能,可以根据这个存储过程算法设计中间表,把存储过程转换为数据库的job,晚上定时来运行。
首先假设有一个表或者试图保存了出入库历史记录V_ST_BILL_FT_GOOD_IN;
这里只简单列一下关键SQL语句的写法:
SELECT
CASE WHEN SD_CREATE_DATE+31>:indate1 THEN 1
WHEN SD_CREATE_DATE+31<=:indate1 AND SD_CREATE_DATE+61>:indate1 THEN 2
WHEN SD_CREATE_DATE+61<=:indate1 AND SD_CREATE_DATE+91>:indate1 THEN 3
WHEN SD_CREATE_DATE+91<=:indate1 AND SD_CREATE_DATE+181>:indate1 THEN 4
WHEN SD_CREATE_DATE+181<=:indate1 AND SD_CREATE_DATE+361>:indate1 THEN 5
WHEN SD_CREATE_DATE+361<=:indate1 AND SD_CREATE_DATE+721>:indate1 THEN 6
WHEN SD_CREATE_DATE+721<=:indate1 AND SD_CREATE_DATE+1081>:indate1 THEN 7
ELSE 8 end as lable,
IN_STOCK.*
FROM V_ST_BILL_FT_GOOD_IN IN_STOCK
WHERE ...... and SD_CREATE_DATE<:in_date2
说明:SD_CREATE_DATE是入库时间,这样就按照库龄分析的时间,首先为各个出入库历史数据加上库龄区间标签
假设我们为临时统计出来的这个表取别名叫IN_STOCK
然后针对临时表IN_STOCK按照各个区间进行数据的汇总。
select SD_SHOP_ID,SD_INVOICE_NO,
sum(decode(lable,1,SD_NUM))NUM1,sum(decode(lable,1,SD_COST_PRICE*SD_NUM))MON1,
sum(decode(lable,2,SD_NUM))NUM2,sum(decode(lable,2,SD_COST_PRICE*SD_NUM))MON2,
sum(decode(lable,3,SD_NUM))NUM3,sum(decode(lable,3,SD_COST_PRICE*SD_NUM))MON3,
sum(decode(lable,4,SD_NUM))NUM4,sum(decode(lable,4,SD_COST_PRICE*SD_NUM))MON4,
sum(decode(lable,5,SD_NUM))NUM5,sum(decode(lable,5,SD_COST_PRICE*SD_NUM))MON5,
sum(decode(lable,6,SD_NUM))NUM6,sum(decode(lable,6,SD_COST_PRICE*SD_NUM))MON6,
sum(decode(lable,7,SD_NUM))NUM7,sum(decode(lable,7,SD_COST_PRICE*SD_NUM))MON7,
sum(decode(lable,8,SD_NUM))NUM8,sum(decode(lable,8,SD_COST_PRICE*SD_NUM))MON8
FROM IN_STOCK
GROUP BY SD_SHOP_ID,SD_INVOICE_NO
这样就统计出来了各个区间的数据了。我们为这个临时表取名叫做IN_STOCK_NUM;
然后统计当前库存,这个很简单就不详细说了,假设得出的临时表名称叫做CURRENT_NUM;把这两部分数据LEFT JOIN到一起。
最后,也就是要直接通过算法来查看到底这个数据落在库龄分析的哪一个区间上。
SELECT STOCK_NAME,PRODUCT_CODE,PRODUCT_NAME,PRODUCT_MODEL,PRODUCT_UNIT,STOCK_NUM,STOCK_MONEY,
DECODE(STOCK_NUM,0,0,ROUND(STOCK_MONEY/STOCK_NUM,2))AVG_PRICE,
case when nvl(num1,0)>STOCK_NUM then STOCK_NUM else nvl(num1,0) end as TOTAL_NUM1,
case when nvl(num1,0)>STOCK_NUM then STOCK_MONEY else nvl(mon1,0) end as TOTAL_MONEY1,
case when nvl(num1,0)
else 0 end as TOTAL_NUM2,
case when nvl(num1,0)
else 0 end as TOTAL_MONEY2,
......依次类推
case when nvl(num1,0)+nvl(num2,0)+nvl(num3,0)+nvl(num4,0)+nvl(num5,0)+nvl(num6,0)+nvl(num7,0)
then STOCK_NUM-(nvl(mon1,0)+nvl(mon2,0)+nvl(mon3,0)+nvl(mon4,0)+nvl(mon5,0)+nvl(mon6,0)+nvl(mon7,0))
else nvl(mon8,0) end
else 0 end as TOTAL_MONEY8
FROM
CURRENT_NUM
LEFT JOIN IN_STOCK_NUM ON .......
剩下的就是对这些数据做数据格式的处理,比如保留小数点后两位数,计算比例等等.