Luis
Luis

Reputation: 1475

MySQL how to get avg of values into specific range

I have the following table data:

value
1
5
10.5
12
36

I want to map these values into:

range      avg
0-21       (1 + 5 + 10.5 + 12) / 4
21.001-34  0
34.001-64  36
64 +       0

Basically map each value to ranges and calculate the avg of all values in each range.

I tried to do:

select 
case
when avggeneral between 0 and 21 then ' 0-21'
when avggeneral between 21.00001 and 34 then ' 21-34'
when avggeneral between 34.00001 and 64 then ' 34-64'
else '64+'
end as 'range',
AVG(*) as 'avg'
from table

but this doesn't work... I don't know how to make the static ranges...

How can I do that in MySQL?

Methology should be: 1. Map values into these groups ( 0-21, 21-34 etc..) 2. calulate AVG in each group.

Desired output for above example is this:

range      avg
0-21       7.125
21.001-34  0
34.001-64  36
64 +       0

The range column is static. Always with 5 rows. The avg column is dynamic.. the values there are the actual AVGs of value column.

Upvotes: 4

Views: 1983

Answers (3)

LukStorms
LukStorms

Reputation: 29667

You don't really need to use those decimals.
Because for example, if the "value" equals 21, then the CASE would already return the 0-21 range before evaluating the next WHEN.

But you still need to group on the range.

And to return all the ranges, whether or not they are missing, you could left join to a sub-query with the ranges.

SELECT Ranges.`range`, COALESCE(AVG(Q.`value`), 0) as `avg`
FROM
(
  SELECT 0 as `class`, ' 0-21' as `range`
  UNION ALL SELECT 21, '21-34'
  UNION ALL SELECT 34, '34-64'
  UNION ALL SELECT 64, '64+'
) Ranges
LEFT JOIN
(
  SELECT 
   `value`,
   case
   when `value` between  0 and 21 then 0
   when `value` between 21 and 34 then 21
   when `value` between 34 and 64 then 34
   when `value` > 64 then 64
   end as rangeclass
  FROM test
) Q ON Q.rangeclass = Ranges.`class`
GROUP BY Ranges.`class`, Ranges.`range`
ORDER BY Ranges.`class`

db<>fiddle here

Upvotes: 2

Salman Arshad
Salman Arshad

Reputation: 272236

You could build a list of ranges using UNION ALL and LEFT JOIN with it:

SELECT CONCAT(IFNULL(ranges.min, '∞'), '-', IFNULL(ranges.max, '∞')) AS `range`, avg(value) AS avg
FROM (
    SELECT 0 AS min, 21 AS max UNION ALL
    SELECT 21, 34 UNION ALL
    SELECT 34, 64 UNION ALL
    SELECT 64, NULL
) AS ranges
LEFT JOIN t ON (ranges.min IS NULL OR value >= ranges.min) AND
               (ranges.max IS NULL OR value <  ranges.max)
GROUP BY ranges.min, ranges.max

Note that the above query will put 20.9999 inside [0-21) and 21.0000 inside [21-34) range.

Upvotes: 3

Md. Suman Kabir
Md. Suman Kabir

Reputation: 5453

You can use UNION to get the desired result like below :

select '0-21' as Range1, coalesce(avg(avggeneral),0) as AVG from Table1
where avggeneral > 0 and avggeneral <= 21
union
select '21-34' as Range1, coalesce(avg(avggeneral),0) as AVG from Table1
where avggeneral > 21 and avggeneral <= 34
union
select '34-64' as Range1, coalesce(avg(avggeneral),0) as AVG from Table1
where avggeneral > 34 and avggeneral <= 64
union
select '64+' as Range1, coalesce(avg(avggeneral),0) as AVG from Table1
where avggeneral > 64

SQL HERE

Upvotes: 3

Related Questions