First, get a rolling sum of quantity and price * quantity. Use a window ordered by price to ensure you spend your money on the best deals first. Since price is not unique, we can also order by the primary key (I'm using name) to ensure consistent results.
Use lead
to keep track of the next possible option, price, and available quantity.
select name, price, qty, sum(qty) over w as "running purchased", sum(price * qty) over w as "running cost", lead(name) over w as "next option", lead(price) over w as "next price", lead(qty) over w as "next option" from items window w as (order by price, name asc rows unbounded preceding)
At this point we have all the information we need to finish the calculation.
- Pick the first row closest to, but not over, your limit.
100 - "running cost"
is the "remaining money"floor( "remaining money" / "next price" )
is the "next purchased""next price" * "next purchased"
is the "next cost"100 - "running cost" - "next cost"
is the "remaining money"
It might be easier to do the final calculation outside of SQL using the results, or you can finish up in SQL...
Use a CTE to pick the row closest to, but not over, your limit and calculate how many can be purchased with your remainder.
Finally, use another CTE to sum this all up and calculate how much money is left over.
with running_total as ( select name, price, qty, sum(qty) over w as "running purchased", sum(price * qty) over w as "running cost", lead(name) over w as "next option", lead(price) over w as "next price", lead(qty) over w as "next option" from items window w as (order by price, name asc rows unbounded preceding)),final_purchase as ( select *, floor( (100 - "running cost") / "next price" ) as "next purchased" from running_total where "running cost" <= 100)select *,"next purchased" * "next price" as "next cost","running purchased" +"next purchased" as "total purchased","running cost" + ("next purchased" * "next price") as "total spent", 100 - ("running cost" + ("next purchased" * "next price")) as "remaining money"from final_purchase
This might have performance advantages over a recursive approach.