Postgresql select 直到达到一定的总量并锁定
Postgresql select until certain total amount is reached and lock
我有 table 个用户批次。我只想 select 直到我的总金额达到一定金额。
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
2 | 2 | 15 | 2
3 | 1 | 8 | 3
4 | 1 | 5 | 4
5 | 2 | 7 | 5
6 | 1 | 1 | 6
7 | 2 | 5 | 7
考虑以下查询:
SELECT * FROM tb_batch_user WHERE user_id = 1 ORDER BY batch_id asc
查询结果为:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
4 | 1 | 5 | 4
6 | 1 | 1 | 6
我想在 table 上做一个 select,直到余额总数为 6。那么应该只返回 ids 1, 2:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
另一个余额总计为 1 的例子。那么应该只返回 ids 1:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
余额总数为 11 的示例。只应返回 ID 1、3、4:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
4 | 1 | 5 | 4
因此,之后我需要使用 FOR UPDATE ex:
锁定这些行
SELECT * FROM tb_batch_user WHERE user_id = 1 ORDER BY batch_id asc FOR UPDATE
我尝试使用 window 函数,但它不允许锁定(更新)。感谢您的帮助。
你在找这个吗?
with w0 as (
select id, user_id, balance, batch_id,
coalesce(lag(running_balance) over (partition by user_id order by batch_id asc), 0) running_balance
from (
SELECT t.* ,
sum(balance) over (partition by user_id order by batch_id asc) running_balance
FROM tb_batch_user t
--where t.user_id = 1
) x
)
select * from w0
where running_balance < 6
PS:可以添加user_id作为where子句。见评论
用于锁定,
select * from tb_batch_user tb
where tb.id in (select w0.id from w0 where running_balance < 6)
for update
这是使用 window 函数的方法:
select id, balance, user_id, batch_id
from (
select t.*,
sum(balance) over(partition by user_id order by id) sum_balance
from mytable t
where user_id = 1
) t
where sum_balance - balance < 6
您需要累积余额,直到第一个余额等于或超过阈值。为此,您可以只使用 window sum()
.
您可以将不等式条件更改为您喜欢的阈值。您还可以更改(或删除)子查询中 user_id
的过滤。
我们可以使用子查询轻松实现相同的逻辑,这将支持 for update
:
select *
from mytable t
where user_id = 1 and (
select coalesce(sum(balance), 0)
from mytable t1
where t1.user_id = t.user_id and t1.id < t.id
) < 6
for update
id | balance | user_id
-: | ------: | ------:
1 | 2 | 1
3 | 8 | 1
假设(user_id, batch_id)
是key,可以使用关联子查询来避免window函数。外部子查询获取最小值 batch_id
,其中 balance
的总和达到或超过给定用户 ID 的 6
。这笔钱是在里面拿的。
SELECT *
FROM tb_batch_user bu1
WHERE bu1.user_id = 1
AND bu1.batch_id <= (SELECT min(bu2.batch_id) batch_id
FROM tb_batch_user bu2
WHERE bu2.user_id = bu1.user_id
AND (SELECT sum(bu3.balance)
FROM tb_batch_user bu3
WHERE bu3.user_id = bu2.user_id
AND bu3.batch_id <= bu2.batch_id) >= 6)
FOR UPDATE;
安装 pgrowlocks
extension 后,我们可以检查是否锁定了正确的行。
SELECT *
FROM pgrowlocks('tb_batch_user');
returns:
locked_row | locker | multi | xids | modes | pids
------------+----------+-------+------------+----------------+---------
(0,1) | 10847645 | f | {10847645} | {"For Update"} | {11996}
(0,3) | 10847645 | f | {10847645} | {"For Update"} | {11996}
我能够 select. . . for update
使用 window 函数:
with inparms as (
select 1 as user_id, 6 as target
), rtotal as (
select t.id, i.target,
sum(t.balance) over (partition by t.user_id
order by t.id
rows between unbounded preceding
and 1 preceding) as runbalance
from tb_batch_user t
join inparms i
on i.user_id = t.user_id
)
select t.*
from rtotal r
join tb_batch_user t
on t.id = r.id
where coalesce(r.runbalance, 0) < r.target
for update of t;
我有 table 个用户批次。我只想 select 直到我的总金额达到一定金额。
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
2 | 2 | 15 | 2
3 | 1 | 8 | 3
4 | 1 | 5 | 4
5 | 2 | 7 | 5
6 | 1 | 1 | 6
7 | 2 | 5 | 7
考虑以下查询:
SELECT * FROM tb_batch_user WHERE user_id = 1 ORDER BY batch_id asc
查询结果为:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
4 | 1 | 5 | 4
6 | 1 | 1 | 6
我想在 table 上做一个 select,直到余额总数为 6。那么应该只返回 ids 1, 2:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
另一个余额总计为 1 的例子。那么应该只返回 ids 1:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
余额总数为 11 的示例。只应返回 ID 1、3、4:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
4 | 1 | 5 | 4
因此,之后我需要使用 FOR UPDATE ex:
锁定这些行 SELECT * FROM tb_batch_user WHERE user_id = 1 ORDER BY batch_id asc FOR UPDATE
我尝试使用 window 函数,但它不允许锁定(更新)。感谢您的帮助。
你在找这个吗?
with w0 as (
select id, user_id, balance, batch_id,
coalesce(lag(running_balance) over (partition by user_id order by batch_id asc), 0) running_balance
from (
SELECT t.* ,
sum(balance) over (partition by user_id order by batch_id asc) running_balance
FROM tb_batch_user t
--where t.user_id = 1
) x
)
select * from w0
where running_balance < 6
PS:可以添加user_id作为where子句。见评论
用于锁定,
select * from tb_batch_user tb
where tb.id in (select w0.id from w0 where running_balance < 6)
for update
这是使用 window 函数的方法:
select id, balance, user_id, batch_id
from (
select t.*,
sum(balance) over(partition by user_id order by id) sum_balance
from mytable t
where user_id = 1
) t
where sum_balance - balance < 6
您需要累积余额,直到第一个余额等于或超过阈值。为此,您可以只使用 window sum()
.
您可以将不等式条件更改为您喜欢的阈值。您还可以更改(或删除)子查询中 user_id
的过滤。
我们可以使用子查询轻松实现相同的逻辑,这将支持 for update
:
select *
from mytable t
where user_id = 1 and (
select coalesce(sum(balance), 0)
from mytable t1
where t1.user_id = t.user_id and t1.id < t.id
) < 6
for update
id | balance | user_id -: | ------: | ------: 1 | 2 | 1 3 | 8 | 1
假设(user_id, batch_id)
是key,可以使用关联子查询来避免window函数。外部子查询获取最小值 batch_id
,其中 balance
的总和达到或超过给定用户 ID 的 6
。这笔钱是在里面拿的。
SELECT *
FROM tb_batch_user bu1
WHERE bu1.user_id = 1
AND bu1.batch_id <= (SELECT min(bu2.batch_id) batch_id
FROM tb_batch_user bu2
WHERE bu2.user_id = bu1.user_id
AND (SELECT sum(bu3.balance)
FROM tb_batch_user bu3
WHERE bu3.user_id = bu2.user_id
AND bu3.batch_id <= bu2.batch_id) >= 6)
FOR UPDATE;
安装 pgrowlocks
extension 后,我们可以检查是否锁定了正确的行。
SELECT *
FROM pgrowlocks('tb_batch_user');
returns:
locked_row | locker | multi | xids | modes | pids
------------+----------+-------+------------+----------------+---------
(0,1) | 10847645 | f | {10847645} | {"For Update"} | {11996}
(0,3) | 10847645 | f | {10847645} | {"For Update"} | {11996}
我能够 select. . . for update
使用 window 函数:
with inparms as (
select 1 as user_id, 6 as target
), rtotal as (
select t.id, i.target,
sum(t.balance) over (partition by t.user_id
order by t.id
rows between unbounded preceding
and 1 preceding) as runbalance
from tb_batch_user t
join inparms i
on i.user_id = t.user_id
)
select t.*
from rtotal r
join tb_batch_user t
on t.id = r.id
where coalesce(r.runbalance, 0) < r.target
for update of t;