1

このサイトを検索して得た最も近いものは次のとおりです 。SQLでの在庫平均コスト計算

しかし残念ながら、それはオラクル固有であり、モデル句を使用していました。

それでは始めましょう。

2 つのテーブルがあります:
-在庫トランザクションを保持する テーブルと
、最新の在庫評価を保持するテーブル

特定の日付に基づく平均原価計算法を使用して、在庫評価レポートを作成しようとしています。最初からその特定の日付までを通常の方法で計算すると、さまざまな応答時間が得られます。
5 年分のデータ (および何千もの異なる在庫品目) を計算することを想像してみてください。かなりの時間がかかります (私の会社はシリコンバレー グレードではありません。つまり、2 コア CPU と 8 GB の RAM のみです)。そのため、最新 (現在) のバックトラックからその特定の日付まで逆算しています。

(毎月、経理部がデータをチェックするので、計算は1か月分のデータのみを永遠に処理します。一貫した不変のパフォーマンスに等しい)


以下のスクリプトでテーブルを1つにマージしました

create table test3 ( rn integer, amt numeric, qty integer, oqty integer);
insert into test3 (rn,amt,qty,oqty) values (0,2260038.16765793,8,0);
insert into test3 (rn,amt,qty,oqty) values (1,1647727.2727,3,0);
insert into test3 (rn,amt,qty,oqty) values (2,2489654.75326715,0,1);
insert into test3 (rn,amt,qty,oqty) values (3,2489654.75326715,0,1);
insert into test3 (rn,amt,qty,oqty) values (4,1875443.6364,1,0);
insert into test3 (rn,amt,qty,oqty) values (5,1647727.2727,3,0);
insert into test3 (rn,amt,qty,oqty) values (6,3012987.01302857,0,1);
insert into test3 (rn,amt,qty,oqty) values (7,3012987.01302857,0,1);

select * from test3; (already sorted desc so rn=1 is the newest transaction)

rn  amt        qty  oqty
0   2260038.168 8   0    --> this is the current average
1   1647727.273 3   0
2   2489654.753 0   1
3   2489654.753 0   1
4   1875443.636 1   0
5   1647727.273 3   0
6   3012987.013 0   1
7   3012987.013 0   1


with recursive 
runsum (id,amt,qty,oqty,sqty,avg) as 
    (select data.id, data.amt, data.qty, data.oqty, data.sqty, data.avg
     from (
        select rn as id,amt,qty, oqty, 
        sum(case when rn=0 then qty else 
             case when oqty=0 then qty*-1 
                else oqty end end) over (order by rn) as sqty, lag(amt) over (order by rn) as avg
          from test3 ) data
         ),
trans (id,amt,qty,oqty,sqty,prevavg,avg) as
    (select id,amt,qty,oqty, sqty,avg,avg
     from runsum 
     union
    select runsum.id,trans.amt,trans.qty, trans.oqty, trans.sqty, lag(trans.avg) over (order by 1), 
    case when runsum.sqty=0 then runsum.amt else 
    ((trans.prevavg*(runsum.sqty+trans.qty))-(runsum.amt*trans.qty)+(trans.prevavg*trans.oqty))/(runsum.sqty+trans.oqty)
    end
    from runsum join trans using (id))
select * 
from trans
where prevavg is null and avg is not null
order by id; 

結果はこのようになっているはずです

rn  amt        qty   oqty   sum avg
1   1647727.273 3   0   5   2627424.705
2   2489654.753 0   1   6   2627424.705
3   2489654.753 0   1   7   2627424.705
4   1875443.636 1   0   6   2752754.883
5   1647727.273 3   0   3   3857782.493
6   3012987.013 0   1   4   3857782.493
7   3012987.013 0   1   5   3857782.493

しかし、代わりに私はこれを得る

id  amt        qty   oqty   sqty    avg
1   1647727.273 3   0   5   2627424.705
2   2489654.753 0   1   6   2627424.705
3   2489654.753 0   1   7   2627424.705
5   1647727.273 3   0   3   3607122.137 --> id=4 is missing thus                       
                                                            screwing the calculation 
                                                       and id=6 in turn dissappears tpp
7   3012987.013 0   1   5   3607122.137

私はびっくりしています。間違いはどこですか?

ご親切にありがとうございました。

編集済み

平均原価計算方法のバックトラッキング (現在の平均を指定して、最後のトランザクションの平均を計算し、n 番目のトランザクションまで同様に計算します)

Avg (n) = ((Avg(n-1) * (Cum Qty(n)+In Qty(n))) - (In Amount(n) * In Qty (n)) + (Avg(n-1) * Out Qty(n))/(Cum Qty(n)+Out Amount(n))

バックトラック トランザクションの累積数量は、インの場合はマイナス、アウトの場合はプラスになります。したがって、現在の数量が 8 の場合、前の数量のトランザクションは 3 であり、そのトランザクションの累積数量は 5 です。

直前の 1 つのトランザクションの平均を計算するには、現在の平均を使用してそのトランザクションの計算に使用します。

@kordirkoの助けによる現在の回答

with recursive 
runsum (id,amt,qty,oqty,sqty,avg) as 
    (select data.id, data.amt, data.qty, data.oqty, data.sqty, data.avg
     from (
        select rn as id,amt,qty, oqty, 
        sum(case when rn=0 then qty else 
             case when oqty=0 then qty*-1 
                else oqty end end) over (order by rn) as sqty, lag(amt) over (order by rn) as avg
          from test3 ) data
         ),
counter (maximum) as
         (select count(rn)
          from test3
         ),
trans (n, id,amt,qty,oqty,sqty,prevavg,avg) as
    (select 0 n, id,amt,qty,oqty, sqty,avg,avg
      from runsum 
     union 
    select trans.n+1, runsum.id,trans.amt,trans.qty, trans.oqty, trans.sqty, 
    lag(trans.avg) over (order by 1), 
    case when runsum.sqty=0 then runsum.amt else 
    ((trans.prevavg*(runsum.sqty+trans.qty))-(runsum.amt*trans.qty)+(trans.prevavg*trans.oqty))/(runsum.sqty+trans.oqty)
    end
    from runsum join trans using (id)
    where trans.n<(select maximum*2 from counter))
select * 
from trans
where prevavg is null and avg is not null
order by id; 
4

1 に答える 1

1

これはおそらくあなたの質問に対する「最良の」答えではありませんが、このトリッキーな問題に苦労しているときに、偶然にもいくつかの醜い回避策にたどり着きました:)。このSQL Fiddle デモ

をクリックします

with recursive 
trans (n, id, amt, qty, oqty, sqty, prevavg, avg) as (
    select 0 n, id, amt, qty, oqty, sqty, avg, avg
    from runsum 
    union 
    select trans.n + 1, runsum.id, trans.amt, trans.qty, trans.oqty, trans.sqty, 
           lag(trans.avg) over (order by 1), 
           case when runsum.sqty=0 then runsum.amt 
                 else 
                 ((trans.prevavg *(runsum.sqty+trans.qty))-(runsum.amt*trans.qty)+(trans.prevavg*trans.oqty))/(runsum.sqty+trans.oqty)
           end
    from runsum 
    join trans using (id)
    where trans.n < 20
)
select * 
from trans
where prevavg is null and avg is not null
order by id; 

問題の原因は、再帰クエリの UNION 句にあるようです。
このリンクを読んでください: http://www.postgresql.org/docs/8.4/static/queries-with.html
彼らは、UNION の再帰クエリは、再帰クエリの評価中に重複行を破棄すると書いています。

于 2013-07-20T01:10:58.967 に答える