views:

83

answers:

3

I have the following MySQL-table

Id  |  One  |  Two  |  Three
----------------------------
1   |  10   |  30   |  20
2   |  50   |  60   |  20
3   |  60   |  NULL |  40

Edit: Of course the table doesn't need to be NULL by default, but I don't want it to affect the average (so the average is calculated as 50 and not 33,33).

I want that to look like this, with a MySQL query:

Id | Average
------------
1  | 20
2  | 43,33
3  | 50

What is the most efficient way to achieve this?

+9  A: 
select id, (ifnull(one,0) + ifnull(two,0) + ifnull(three,0))/
  ((one is not null) + (two is not null) + (three is not null)) as average from table
Looks good but watch out for divide by zero if all 3 are null.
Pool
Revised my comment. `select 0/0;` actually returns `NULL`. I had no idea ... this answer is perfect
Cassy
Yes, excellent answer. Welcome to Stack Overflow jxac :)
Daniel Vassallo
+3  A: 

The obvious option below would have worked if you had no NULL values:

SELECT ((col_1 + col_2 + col_3) / 3) AS Average FROM table;

However, as jxac suggested in another answer, you would have to do it as follows:

SELECT id, (ifnull(col_1, 0) + ifnull(col_2, 0) + ifnull(col_3, 0)) /
  ((col_1 is not null) + (col_2 is not null) + (col_3 is not null)) as Average 
FROM
    table;

If all the values of each column were to be NULL, the division by zero would return NULL for that row.

Daniel Vassallo
@Eikern: Is there any reason why this is not appropriate for your problem?
Daniel Vassallo
this wouldn't work in the third row: `SELECT 60 + NULL + 40` returns `NULL`. And the result would be wrong too: The average of 60 and 50 is 50, as in the example, your query would return 33.3 (if it wouldn't return `NULL` obviously)
Cassy
@Cassy: Thanks for that. Didn't notice the NULL value. Edited my answer to include jxac's alternative.
Daniel Vassallo
+2  A: 

Use:

  SELECT a.id,
         AVG(a.val) AS average
    FROM (SELECT x.id,
                 x.one AS val
            FROM TABLE x
          UNION ALL
          SELECT y.id,
                 y.two AS val
            FROM TABLE y
          UNION ALL
          SELECT z.id,
                 z.three AS val
            FROM TABLE z) a
GROUP BY a.id

Reference:

OMG Ponies
+1 for organized labor, I mean UNIONs!
pilcrow