计算库存的平均数量

时间:2016-06-30 20:54:08

标签: sql sql-server

我试图通过平均每天的结束数量,在参数@StartDate的日期范围内找到库存的平均数量。我有三个表:零件表,零件交易表和仓库表,在下面模拟。

PartNum      |   PartNum     TranDate     TranQty      |   PartNum   OnHandQty
----------   |   ------------------------------------  |  --------------------
P1           |   P1          6/28/2016    5            |   P1        30
P2           |   P1          6/26/2016    3            |   P2        2
             |   P1          6/26/2016    -1           |
             |   P1          6/15/2016    2            |
             |   P2          6/15/2016    1            |

如果今天是2016年6月30日和@StartDate = 2016年6月1日,我希望得到如下结果:

PartNum    AverageOnHand
------------------------
P1         22.9
P2         1.5

但是,我不知道什么功能最能让我得到一个合适的加权和,我可以除以日期的差异。是否有SumProduct功能或类似功能,我可以在这里使用?到目前为止,我的代码如下:

select 
    [Part].[PartNum] as [Part_PartNum],
    (max(PartWhse.OnHandQty)*datediff(day,max(PartTran.TranDate),Constants.Today)) as [Calculated_WeightedSum],
    (WeightedSum/DATEDIFF(day, @StartDate, Constants.Today)) as [Calculated_AverageOnHand]
from Erp.Part as Part
right outer join Erp.PartTran as PartTran on 
    Part.PartNum = PartTran.PartNum

inner join Erp.PartWhse as PartWhse on 
    Part.PartNum = PartWhse.PartNum

group by [Part].[PartNum]

3 个答案:

答案 0 :(得分:2)

撤消交易以计算每日数量。添加缺失日期并向后查看最近填写每日数量的日期。我想我会尝试一个比这个更好的解决方案。

http://rextester.com/JLD19862

with trn as (
    select PartNum, TranDate, TranQty from PartTran
    union all
    select PartNum, cast('20160601' as date), 0 from PartWhse
    union all
    select PartNum, cast('20160630' as date), 0 from PartWhse
), qty as (
    select
        t.PartNum, t.TranDate,
        -- assumes that end date corresponds with OnHandQty
        min(w.OnHandQty) + sum(t.TranQty)
            - sum(sum(t.TranQty))
                over (partition by t.PartNum order by t.TranDate desc) as DailyOnHand,
        coalesce(
            lead(t.TranDate) over (partition by t.PartNum order by t.TranDate),
            dateadd(day, 1, t.TranDate)
        ) as NextTranDate
        -- if lead() isn't available...
        -- coalesce(
        --    (
        --        select min(t2.TranDate) from trn as t2
        --        where t2.PartNum = t.PartNum and t2.TranDate > t.TranDate
        --    ),
        --    dateadd(day, 1, t.TranDate)
        -- ) as NextTranDate
    from PartWhse as w inner join trn as t on t.PartNum = w.PartNum
    where t.TranDate between '20160601' and '20160630'
    group by t.PartNum, t.TranDate
)
select
    PartNum,
    sum(datediff(day, TranDate, NextTranDate) * DailyOnHand) * 1.00
        / sum(datediff(day, TranDate, NextTranDate)) as DailyAvg
from qty
group by PartNum;

答案 1 :(得分:2)

这是一个有趣的sql-server 2012 +方法。

;WITH cte AS (
    SELECT
       p.PartNum
       ,CAST(t.TranDate AS DATE) AS TranDate
       ,i.OnHandQty
       --,SUM(SUM(t.TranQty)) OVER (PARTITION BY p.PartNum ORDER BY CAST(t.TranDate AS DATE) DESC) AS InventoryChange
       ,i.OnHandQty - SUM(SUM(t.TranQty)) OVER (PARTITION BY p.PartNum ORDER BY CAST(t.TranDate AS DATE) DESC) AS InventoryOnDate
       ,DATEDIFF(day,
          CAST(ISNULL(LAG(MAX(TranDate)) OVER (PARTITION BY p.PartNum ORDER BY CAST(t.TranDate AS DATE) ASC),@StartDate) AS DATE)
          ,CAST(t.TranDate AS DATE)
       ) AS DaysAtInventory
    FROM
       #Parts p
       LEFT JOIN #Transact t
       ON p.PartNum = t.PartNum
       LEFT JOIN #Inventory i
       ON p.PartNum = i.PartNum
    GROUP BY
       p.PartNum
       ,CAST(t.TranDate AS DATE)
       ,i.OnHandQty
)

SELECT
    PartNum
    ,(SUM(ISNULL(DaysAtInventory,0) * ISNULL(InventoryOnDate,0))
    + ((DATEDIFF(day,MAX(TranDate),CAST(GETDATE() AS DATE)) + 1) * ISNULL(MAX(OnHandQty),0)))
    /((DATEDIFF(day,CAST(@StartDate AS DATE),CAST(GETDATE() AS DATE)) + 1) * 1.00) AS AvgDailyInventory
FROM
    cte
GROUP BY
    PartNum

这个实际上给了我22.9但是引入了3.33333 333,因为1天必须放在某处,所以我把它当作当前的库存。

这是我之前回答的一个方法,这个方法更容易概念化数据......我会对两种方法之间的性能差异感到好奇。

这些步骤中的一些可以结合起来更简洁但是这有效(虽然我得到了22.6而不是.1或.9 ....)我把所有内容都整理成了一个完整的日期,这样你就不会# 39;不得不担心一天的开始和结束。

DECLARE @StartDate DATETIME = '6/1/2016'

;WITH cteDates AS (
    SELECT @StartDate AS d
    UNION ALL
    SELECT
       d + 1 AS d
    FROM
       cteDates c
    WHERE c.d + 1 <= CAST(CAST(GETDATE() AS DATE) AS DATETIME)
    --get dates to today beginning of day
)

, ctePartsDaysCross AS (
    SELECT
       d.d
       ,p.PartNum
       ,ISNULL(i.OnHandQty,0) AS OnHandQty
    FROM
       cteDates d
       CROSS JOIN #Parts p
       LEFT JOIN #Inventory i
       ON p.PartNum = i.PartNum
)

, cteTransactsQuantityByDate AS (
    SELECT
       CAST(t.TranDate AS DATE) as d
       ,t.PartNum
       ,TranQty = SUM(t.TranQty)
    FROM
       #Transact t
    GROUP BY
       CAST(t.TranDate AS DATE)
       ,t.PartNum
)

,cteDailyInventory AS (
    SELECT
       c.d
       ,c.PartNum
       ,c.OnHandQty - SUM(ISNULL(t.TranQty,0)) OVER (PARTITION BY c.PartNum ORDER BY c.d DESC) AS DailyOnHand
    FROM
       ctePartsDaysCross c
       LEFT JOIN cteTransactsQuantityByDate t
       ON c.d = t.d
       AND c.PartNum = t.PartNum
)

SELECT
    PartNum
    ,AVG(CAST(DailyOnHand AS DECIMAL(6,3)))
FROM
    cteDailyInventory
GROUP BY
    PartNum

以下是测试数据:

IF OBJECT_ID('tempdb..#Parts') IS NOT NULL
    BEGIN
        DROP TABLE #Parts
    END

IF OBJECT_ID('tempdb..#Transact') IS NOT NULL
    BEGIN
        DROP TABLE #Transact
    END

IF OBJECT_ID('tempdb..#Inventory') IS NOT NULL
    BEGIN
        DROP TABLE #Inventory
    END

CREATE TABLE #Parts (
    PartNum CHAR(2)
)

CREATE TABLE #Transact (
    AutoId INT IDENTITY(1,1) NOT NULL
    ,PartNum CHAR(2)
    ,TranDate DATETIME
    ,TranQty INT
)

CREATE TABLE #Inventory (
    PartNum CHAR(2)
    ,OnHandQty INT
)

INSERT INTO #Parts (PartNum) VALUES ('P1'),('P2'),('P3')

INSERT INTO #Transact (PartNum, TranDate, TranQty)
VALUES ('P1','6/28/2016',5),('P1','6/26/2016',3),('P1','6/26/2016',-1)
,('P1','6/15/2016',2) ,('P2','6/15/2016',1)

INSERT INTO #Inventory (PartNum, OnHandQty) VALUES ('P1',30),('P2',2)

我在想1递归cte可能更简单,可能会将其作为更新发布。

答案 2 :(得分:0)

我能用一笔钱来解决这个问题。首先,我将手头的最终数量乘以该范围内的天数。接下来,我将每个库存变化乘以从@StartDateTransDate的时间。

select 
    [Part].[PartNum] as [Part_PartNum],
    (max(PartWhse.OnHandQty)*datediff(day,@StartDate,Constants.Today)-
         sum(PartTran.TranQty*datediff(day,@StartDate,PartTran.TranDate))) as [Calculated_WeightedSum],
    (WeightedSum/DATEDIFF(day, @StartDate, Constants.Today)) as [Calculated_AverageOnHand]
from Erp.Part as Part
right outer join Erp.PartTran as PartTran on 
    Part.PartNum = PartTran.PartNum

inner join Erp.PartWhse as PartWhse on 
    Part.PartNum = PartWhse.PartNum

group by [Part].[PartNum]

感谢大家的帮助!你真的帮我思考过了。