2016-06-30 7 views
1

私は毎日の終了数量を平均化することにより、@StartDateの日付範囲で自分の在庫の平均数量を求めようとしています。私は3つのテーブルを持っています:パーツテーブル、パーツトランザクションテーブル、そしてウェアハウステーブル。しかし、私にはわからない在庫の手元にある平均数を計算してください

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

何の機能最高の私にできるようになる:今日は2016年6月30日と@StartDate = 2016年6月1日である場合

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   | 

、私のような結果が予想されます私は日付の違いで分けることができる適切な加重和を得るために。ここで使用できる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

rdbms(sql-server、oracle、mysql、postgresql) – Matt

+4

また、あなたのサンプルテーブルを使用すると、あなたは22.1に到着した方法を私に見せてもらえますか(私はあなたの式ではない構文が不思議です)。だから倉庫のテーブルは常に現在の数量ですか?正の場合はその数量に加算したり減算したりすると、取引表が表示されます。言い換えれば、正の整数は売却かリターンですか? – Matt

+0

@Matt SQL Server。あなたはそれを正しく持っています、倉庫のテーブルは現在の数量を返します。追加すると、トランザクションテーブルは正の値になります。したがって、2016年6月27日の終わりには、手元に25個のP1がありました。私は22を得た。1日の終わりに利用可能な数量を計算し、合計を取って数で割ることによって、1を計算する。この結果は、月、時、または連続ではなく、日ごとの平均値になります。 – soytsauce

答えて

0

私はこれを合計で解決することができました。まず、最終的な数量を範囲内の日数で乗算しました。次に、在庫の各変更に@StartDateからTransDateまでの時間を掛けました。

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] 

ご協力いただきありがとうございます。あなたは本当に私がそれを考えるのを手伝った。

+0

「WeightedSum」はどこから来たの?それはそれの前の表現と同じであるはずですか?私はあなたの質問を私と一致させることができませんでした。 – shawnt00

+0

あなたの質問を正しく読んでいれば((OnHand * TotalDaysDiff)+(TransactionQuantity * DaysDiffFromStart))/ TotalDaysDiff?それは間違いなくDailyOnHandの平均値ではありません。 (30 * 29日)+((2 * 14)+( - 1 * 25)+(3 * 25)+(5 * 27)))/ 29となるP1を取ると、 37.34483 ............................................ – Matt

+0

@ shawnt00私のアイデア最初に平均の分子を解き、分母で除算することでした。トラブルシューティング中に役立つことがわかった。 – soytsauce

2

トランザクションを逆にして、1日の数量を計算します。不足している日付を追加し、最新の日付に逆戻りして日々の数量を入力します。私はこれよりも優れた解決策を模索しようと考えています。ここで

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; 
2

面白いです、SQLサーバー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 

この1

は実際に私の1日は、どこかに置くので、私は現在の在庫としてそれを立ち往生しているので、333が導入されますが、22.9 1.53333を与えました。

これまで私が回答したこの方法は、データを概念化するのが少し簡単です。私は2つの方法のパフォーマンスの違いについて興味があります。

これらのステップのいくつかを組み合わせることで、少し簡潔にすることができますが、これはうまくいきます(22.6ではなく.1または.9 ...)。これを実行している間、日の始まりと終わりを心配する必要はありません。ここで

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がアップデートとしてその投稿可能性が単純であるかもしれない考えています。