Reputation: 305
I have a table of user batches. I only want to select until my amount total reaches a certain amount.
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
Consider the following query:
SELECT * FROM tb_batch_user WHERE user_id = 1 ORDER BY batch_id asc
The result of query is:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
4 | 1 | 5 | 4
6 | 1 | 1 | 6
I want to do a select on the table until the balance total is 6. Then only ids 1, 2 should be returned:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
Another example with balance total 1. Then only ids 1 should be returned:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
Example with balance total 11. Only ids 1,3,4 should be returned:
id | user_id | balance | batch_id
----|---------|-------- |--------
1 | 1 | 2 | 1
3 | 1 | 8 | 3
4 | 1 | 5 | 4
So, after that I need to lock those lines with FOR UPDATE ex:
SELECT * FROM tb_batch_user WHERE user_id = 1 ORDER BY batch_id asc FOR UPDATE
I tried with window function, but it doesn't allow lock (FOR UPDATE). Thanks for any help.
Upvotes: 1
Views: 354
Reputation: 222682
Here is an approach using window functions:
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
You want a cumulative balance until the first one that equals or exceeds the threshold. For this, you can just use a window sum()
.
You can change the inequality condition to the threshold value that you like. You can also change (or remove) the filtering on user_id
in the subquery.
We can easily implement the same logic with a subquery, that would support 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
Upvotes: 1
Reputation: 30663
are you looking for this?
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: you can add user_id as where clause. see comment
for locking,
select * from tb_batch_user tb
where tb.id in (select w0.id from w0 where running_balance < 6)
for update
Upvotes: 1
Reputation: 12494
I am able to select. . . for update
using window functions:
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;
Upvotes: 1
Reputation: 37497
Assuming that (user_id, batch_id)
is a key, you can use correlated subqueries to avoid window functions. The outer subquery gets the minimal batch_id
where the sum of balance
reaches or exceeds 6
for the given user id. That sum is fetched in the inner one.
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;
With the pgrowlocks
extension installed, we can check that the right rows are locked.
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}
Upvotes: 0