合并多个UPDATE查询

时间:2016-12-02 07:00:44

标签: postgresql

我想将多个UPDATE查询合并到PostgreSQL中的单个查询中。

以下是相关表格的架构: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分。然后我会执行以下更新:

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

如果我在第二天(12月2日)获得30分,我会执行以下INSERT:

INSERT INTO pointDailyHistory (deposit, balance, depositday) VALUES (30,30,'2016-12-02');
到目前为止很容易。现在,提款。为了满足上述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 个答案:

答案 0 :(得分: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(联合的下半部分)将此信息传递给下一次迭代。下半部分找到的下一条记录晚于刚刚选择的记录,并且还有余额。它重复查找需要更新的所有行,直到剩余撤销金额已完全分配给各种记录。

这会构建一个具有

的块CTE行集
depositday balance depth removed remaining
2016-12-01   150     1      150      10
2016-12-02   30      2       10       0

最后,它使用CTE行集来驱动存款日字段上的更新匹配。

我还没有做过任何形式的效果分析。我希望查询计划很有趣,所以你要自己测试一下。

https://www.postgresql.org/docs/current/static/queries-with.html

上的CTE文件