2016-12-02 7 views
0

複数のUPDATEクエリをPostgreSQLの1つのクエリに統合したいと思います。ここで複数のUPDATEクエリを統合する

は、問題のテーブルのスキーマです:pointDailyHistory

id   | integer | not null default nextval('pointdailyhistory_id_seq'::regclass) 
deposit | integer | not null default 0 
withdrawal | integer | not null default 0 
balance | integer | not null default 0 
depositday | date | not null 
Indexes: 
    "pointdailyhistory_depositday_key" UNIQUE, btree (depositday) 

は、この表の目的は、日常的に(撤退)消費(預金)獲得ポイントとポイントを追跡することです。その理由は、最初に獲得したポイントが最初に消費されるポリシー(FIFO)を有効にすることです。

12月1日に100ポイントを獲得したとします。次に、次のINSERTを実行します。

INSERT INTO pointDailyHistory (deposit, balance, depositday) VALUES (100,100,'2016-12-01'); 

同じ日に別の50ポイントを獲得したとします。

INSERT INTO pointDailyHistory (deposit, balance, depositday) VALUES (30,30,'2016-12-02'); 

簡単にこれまでのところ:私は次の日(12月2日)で30ポイントを獲得した場合、私は以下のINSERTを実行することになり

UPDATE pointDailyHistory SET deposit=deposit+50,balance=balance+50 WHERE depositday='2016-12-01'; 

:私は、次のUPDATEを実行することになります。今、引き出しのために。上記のFIFOを満たすために、 私は残高がゼロよりも大きい最も古い日に払い戻しを実行したいと思います。私は12月3日に160ポイントを撤回した場合ので、私は以下を実行したい

UPDATE pointdailyhistory SET withdrawal=150,balance=0 WHERE depositday='2016-12-01'; 
UPDATE pointdailyhistory SET withdrawal=10,balance=20 WHERE depositday='2016-12-02'; 

少数の行を更新するときにこれが細かいですが、UPDATE文の数があればポイントが急騰します幅広い日付の間に得られ、1日で消費されます。

どのようなヒント、解決策が大歓迎です!

+1

CTEのドキュメント私は最初の場所でバランスを保管しないだろうと私は '同じ日のために既存の行をupdate'ません - 唯一の新しい行を挿入します。残高にすばやくアクセスする必要がある場合は、残高のみを保持する第2のテーブルを作成し、pointdailyhistoryテーブルのトリガーで更新してください。 –

+0

こんにちは、迅速な返信に感謝します。私はあなたの第2のポイントを理解しています。上記のようには言及していませんが、最新の残高を把握するためのバランステーブルがあります。 UPDATINGではなくINSERTを使用しています - 私は考えを出し、それが自分の要件を満たしているかどうかを確認します。あなたの提案をありがとう。 – kiseragi

+1

バランステーブルを既に持っているなら、なぜそれを 'pointdailyhistory'に残しておくのですか? –

答えて

0

これは、再帰的なCTEを使用して単一のSQLとして実行できます。

with recursive 
    required(amount) as (values (160)), 
    chunk (depositday, balance, depth, removed, remaining) 
    as 
    (
    select 
    depositday,balance,1,least(x.balance,amount), amount - least(x.balance,amount) 
    from pointdailyhistory x, required 
    where x.balance > 0 and 
     not exists 
     (select * from pointdailyhistory x2 
      where x2.balance > 0 and 
       x2.depositday < x.depositday) 
    union all 
    select x.depositday,x.balance,chunk.depth + 1, 
    least(x.balance, chunk.remaining), 
    chunk.remaining - least(x.balance, chunk.remaining) 
    from pointdailyhistory x 
    inner join chunk on 
     chunk.depositday < x.depositday and chunk.remaining > 0 
    where 
     x.balance > 0 and 
     not exists (select * from pointdailyhistory x2 
      where x2.balance > 0 and x2.depositday < x.depositday 
        and x2.depositday > chunk.depositday) 
) 
update pointdailyhistory 
set withdrawal = pointdailyhistory.withdrawal + chunk.removed, 
    balance = pointdailyhistory.balance - chunk.removed 
from chunk 
where pointdailyhistory.depositday = chunk.depositday; 

チャンクCTEは、残高を持っている最も古いエントリを見つけることによって更新される最初のpointdailyrecordエントリーをピックアップ。次に、そのエントリを削除できるかどうかを判断します。次に、この情報をCTE(後半の組合)を通じて次の反復に渡します。後半には、選択したレコードよりも遅く次のレコードがあり、バランスも取られています。これは、取り消された金額が様々なレコードに完全に割り当てられるまで、更新する必要があるすべての行を見つけることを繰り返します。

これは

depositday balance depth removed remaining 
2016-12-01 150  1  150  10 
2016-12-02 30  2  10  0 

最後に、それはdepositdayフィールド上のアップデートマッチングを駆動することCTE行セットを使用しているチャンクCTE行セットを構築します。

私はパフォーマンス分析のどのような形式も行っていません。クエリプランが面白いと思うので、自分でテストしたいと思うでしょう。 https://www.postgresql.org/docs/current/static/queries-with.html

+0

こんにちはゲイリー、華麗な答えをありがとう。これは私が探していたものです。ライブを使用する前にパフォーマンステストを行います。 – kiseragi

+0

喜んで私が助けることができました、あなたが必要とするものがあれば、それを受け入れるように私の答えに印を付けてください。 – Gary

+0

こんにちはゲイリー、そうした!あなたの助けをもう一度ありがとう! – kiseragi

関連する問題