我想将多个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语句的数量会急剧增加。
任何提示,解决方案都将受到欢迎!
答案 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文件