Home > Back-end >  Total Percentage Not Adding up to 100 (postgresql)
Total Percentage Not Adding up to 100 (postgresql)

Time:11-12

Need your feedback please. I am struggling with the following code to make percentage equal to 100.

SELECT animaltype, size, SUM(total) AS total,
ROUND(( SUM(total) * 100 / SUM( SUM(total)) OVER ()),2) AS percentage
FROM animals
WHERE sponsored_animalid IS NULL
GROUP BY animaltype, size
ORDER BY animaltype, size DESC;

This is the output, which equates to 99.99% hence making the query incorrect. Result Table

I need the percentage column to be rounded upto 2 decimal places, but total needs to add up to 100. I dont know what is the bug?

As soon as I edit ROUND( ...,3) - the code adds up to 100. But I need the figures to be rounded up to 2 decimal places strictly.

Here is the output when I round up to 3 decimal places and total is 100: Roundup 3 places

CodePudding user response:

Your SQL statement is syntactically incorrect, but I get what you mean.

That's not a perfect solution, but perhaps you can reduce the average rounding error by using a rounding function that round to the nearest even number rather than away from zero:

CREATE FUNCTION myround(numeric, integer) RETURNS numeric
   LANGUAGE sql IMMUTABLE AS
'SELECT CASE WHEN abs($1 * 10::numeric ^ $2 % 1) = 0.5
            THEN round($1 * 10::numeric ^ $2 / 2, 0)
                 * 2 / 10::numeric ^ $2
            ELSE round($1, $2)
       END';

For a really perfect solution you would have to look at this question.

CodePudding user response:

Possible practical solution to your issue is rounding the final aggregate by one decimal place less than the rounding on the previous more granular level

INSERT INTO animals (animaltype, size, total)
VALUES 
('Bird', 'Small', 1615),('Bird', 'Medium', 3130),('Bird', 'Large', 8120),
('Cat', 'Small', 518015),('Cat', 'Medium', 250575),('Cat', 'Large', 439490),
('Dog', 'Small', 336680),('Dog', 'Medium', 942095),('Dog', 'Large', 978115);
        
SELECT
animaltype, 
size,
total,
percentage_exact,
percentage_rd2,
sum(percentage_exact) over ()  as all_perc_exact_sum,
sum(percentage_rd2) over ()  as   all_perc_rd2_sum,
round(sum(percentage_rd2) over ()  ,1) as all_perc_rd2_sum_rd1
FROM
  (
  SELECT  
  animaltype, 
  size,
  total,
  100.0 * total / (SUM(total) over ()) as percentage_exact,  
  round(100.0 * total / (SUM(total) over ()),2)  as percentage_rd2
  FROM animals       
  ) s


| animaltype |   size |  total |     percentage_exact | percentage_rd2 | all_perc_exact_sum | all_perc_rd2_sum | all_perc_rd2_sum_rd1 |
|------------|--------|--------|----------------------|----------------|--------------------|------------------|----------------------|
|       Bird |  Small |   1615 | 0.046436935622305255 |           0.05 |                100 |            99.99 |                  100 |
|       Bird | Medium |   3130 |  0.08999851919369378 |           0.09 |                100 |            99.99 |                  100 |
|       Bird |  Large |   8120 |  0.23347858653443881 |           0.23 |                100 |            99.99 |                  100 |
|        Cat |  Small | 518015 |    14.89475492655632 |          14.89 |                100 |            99.99 |                  100 |
|        Cat | Medium | 250575 |    7.204913401584607 |            7.2 |                100 |            99.99 |                  100 |
|        Cat |  Large | 439490 |   12.636884728573955 |          12.64 |                100 |            99.99 |                  100 |
|        Dog |  Small | 336680 |     9.68073528502646 |           9.68 |                100 |            99.99 |                  100 |
|        Dog | Medium | 942095 |   27.088547904084006 |          27.09 |                100 |            99.99 |                  100 |
|        Dog |  Large | 978115 |   28.124249712824213 |          28.12 |                100 |            99.99 |                  100 |


So here round first by 2 decimal place

round(100.0 * total / (SUM(total) over ()),2)  as percentage_rd2

and then round the aggregate by 1 decimal place

round(sum(percentage_2) over ()  ,1) as   all_perc_rd2_sum_rd1
  • Related