I am working on a big (not real) task to manage the expenses of several countries. I have already calculated the capacities of every town in investments, now I need to calculate the budget to built these spaceships. The task is as follows:
We have the tables below (there are tables Town and Spaceship, but the task is clear without them here). We need to calculate how much money is needed to complete each type of ship available for production. So, we have different types of spaceships and each type needs different types of parts (see table Spaceship_required_part). In every town there are produced several types of parts (see table Spaceship_part_in_town). We need to calculate, what is the cost (see cost in Spaceship_part, stage in Spaceship_part_in_town, and amount in Spaceship_required_part) to build a unit of every available type of spaceship. By available we mean that the parts needed can be found in the given city. We calculate the budget for a given city (I can do it for the rest of them by myself).
create table Spaceship_part(
id int PRIMARY KEY,
name text,
cost int
);
create table Spaceship_part_in_town(
id int PRIMARY KEY,
spaceship_part_id int references Spaceship_part,
city_id int references Town,
stage float -- the percentage of completion of the part
);
create table Spaceship_required_part(
id int PRIMARY KEY,
spaceship_part int references Spaceship_part,
spaceship int references Spaceship,
amount int -- amount of a particular part needed for the given spaceship
);
I understand how would I solve this task using a programming language, but my SQL skills are not that good. I understand that first I need to check what spaceships can we build using the available parts in the town. This can be done using a counter of the needed parts (amount
) and available parts in town (count(spaceship_part_id)
). Then I need to calculate the sum needed to build every spaceship using the formula (100-stage)*cost/100
.
However, I have no idea how to compose this in SQL code. I am writing in PostgreSQL.
CodePudding user response:
The data model is like:
To build a spaceship with least build cost, we can:
Step 1. Calculate a part's build_cost
= (100 - stage) * cost / 100; for each part, rank the build cost based on stage so we minimize total cost for a spaceship.
Step 2. Based on build_cost
, we calcualte the total_cost
of a parts by required quantities (in order to compare with spaceship_required_part.amount
) and take notes from where the parts are coming from in part_sources
, which is in CSV format (city_id, stage, build_cost),...
Step 3. Once we have available parts and total qty & cost calculate, we join it with spaceship_required_part to get result like this:
spaceship_id|spaceship_part_id|amount|total_cost|part_sources |
------------ ----------------- ------ ---------- ---------------------
1| 1| 2| 50.0|(4,80,20),(3,70,30) |
1| 2| 1| 120.0|(1,40,120) |
2| 2| 2| 260.0|(1,40,120),(2,30,140)|
2| 3| 1| 180.0|(2,40,180) |
3| 3| 2| 360.0|(2,40,180),(4,40,180)|
The above tells us that to build:
- spaceship#1, we need part#1 x 2 sourced from city#4 and city#3; part#2 x 1 from city 1; total cost = 50 120 = 170, or
- spceeship#2, we need part#2 x 2 sourced from city#1 and city#2; part#3 x 1 from city#2; total cost = 160 180 = 340, or
- spaceship#3, we need part#3 x 2 from city#2 and city#4; total cost = 360.
After 1st iteration, we can update spaceship_part_in_town
and remove the 1st spaceship from spaceship_required_part
, then run the query again to get the 2nd spaceship to build and its part sources.
with cte_part_sources as (
select spt.spaceship_part_id,
spt.city_id,
sp.cost,
spt.stage,
(100.0-spt.stage)*sp.cost/100.0 as build_cost,
row_number() over (partition by spt.spaceship_part_id order by spt.stage desc) as cost_rank
from spaceship_part_in_town spt
join spaceship_part sp
on spt.spaceship_part_id = sp.id),
cte_parts as (
select spaceship_part_id,
city_id,
cost_rank,
cost,
stage,
build_cost,
cost_rank as total_qty,
sum(build_cost) over (partition by spaceship_part_id order by cost_rank) as total_cost,
string_agg('(' || city_id || ',' || stage || ',' || build_cost || ')',',') over (partition by spaceship_part_id order by cost_rank) as part_sources
from cte_part_sources)
select srp.spaceship_id,
srp.spaceship_part_id,
srp.amount,
p.total_cost,
p.part_sources
from spaceship_required_part srp
left
join cte_parts p
on srp.spaceship_part_id = p.spaceship_part_id
and srp.amount = p.total_qty;
EDIT:
- added db fiddle