I have a category, percentage table in SQL (the table itself has been obtained by the following query:
SELECT p.category,
sum(p.sales) * 100 / sum(sum(p.sales)) OVER () AS percentage
FROM product p
GROUP BY p.category;
Category | Percentage |
---|---|
Books | 20.51% |
Clocks | 9.49% |
Pens | 60.2% |
Desks | 9.8% |
How can I spread $100 (whole dollars, no cents) across the categories by percentage keeping in mind the GROUP BY and windows functions already in the query.
Intended Solution:
Category | Percentage | Amount |
---|---|---|
Books | 20.51% | 21 |
Clocks | 9.49% | 9 |
Pens | 60.2% | 60 |
Desks | 9.8% | 10 |
I have been googling but I can’t seem to find a similar scenario.
2
Answers
You could just
round()
the percentages you already have because they already sum up nicely to a 100 when rounded, but that’s becauseBooks
round up andClocks
round down, balancing each others’ rounding errors out. If you lose that balance, you’ll getAmounts
summing up to less than a 100 or more than a 100: demoThis distributes the rounding remainders evenly. You can also round all values until the last one and make the last one catch the remainders. Some banks and insurance companies prefer to set up installments this way:
Use of
floor()
keeps the remainder sum positive to avoid that last one dropping below 0.To split $100 across categories by percentage as fairly as possible:
fiddle
The specific difficulty is that rounded percentages can sum up to more or less than $100. So assign the minimum share (= truncated percentage) in a first round, and then add $1 to the highest remainders in order until the $100 are spent.
If categories tie for a remaining buck (identical percentages), alphabetic sort order serves as tiebreaker in my query. Adjust any way you want.
The fiddle has more details.
I based my query on a single table
products
, as presented in the question. In actuality, there will be at least a second tableorders
in a 1:n relationship. Adjust accordingly. The principle query is the same.