0

I have a SQL table that has 8 columns that are empirical results for the same process. However, some of the data is incomplete meaning that there are cases where some of the of the 8 columns have null instead of a numerical value. I know that if all 8 columns were present I could do something like ...

SELECT (val1+val2+val3+val4+val5+val6+val7+val8)/8 from mytable;

But if say two of the values are null what I want in that case is ...

SELECT (val1+val2+val3+val4+val5+val6)/6 from mytable;

How do I do this? The only other posts I have seen regarding this query (no pun intended) are to do with simply parsing out any null values.

4
  • 2
    Tag your question with the database you are using. Commented Dec 18, 2019 at 22:48
  • Does this answer your question? Strange MySQL AVG() anomaly NULL values Commented Dec 18, 2019 at 22:49
  • 1
    Pivot so your data is in a single column/multiple rows and use AVG. How you do that is DB dependent. What you have is a bad table design - it makes you have to do these things.... Commented Dec 18, 2019 at 22:50
  • Does this answer your question? SQL: sum 3 columns when one column has a null value? Commented Dec 18, 2019 at 22:52

3 Answers 3

1
 SELECT sum(COALESCE(val1,0)
          + COALESCE(val2,0)
          + COALESCE(val3,0)
          + COALESCE(val4,0)
          + COALESCE(val5,0)
          + COALESCE(val6,0)
          + COALESCE(val7,0)
          + COALESCE(val8,0)) / 8 AS total FROM yourTable
Sign up to request clarification or add additional context in comments.

Comments

0

You would probably be better off putting the data in separate rows. You can do this with some arithmetic:

SELECT (coalesce(val1, 0) + coalesce(val2, 0) + . .  .) /
       (case when val1 is not null then 1 else 0 end +
        case when val2 is not null then 1 else 0 end +
        . . .
       ) as average
FROM mytable;

If you database supports lateral joins (such as SQL Server, Oracle, and Postgres), then syntax such as this is simpler:

select t.*, v.average
from t cross join lateral
     (select avg(v.val) as average
      from (values (val1), (val2), . . . ) v(val)
     ) v;

Comments

0

One cross-database option uses coalesce() and case expressions. This assumes that not all of the values will be null on the same record.

select 
    (
        coalesce(val1, 0)
        + coalesce(val2, 0)
        + coalesce(val3, 0)
        + coalesce(val4, 0)
        + coalesce(val5, 0)
        + coalesce(val6, 0)
        + coalesce(val7, 0)
        + coalesce(val8, 0)
    ) / (
        case when val1 is null then 0 else 1 end
        + case when val2 is null then 0 else 1 end
        + case when val3 is null then 0 else 1 end
        + case when val4 is null then 0 else 1 end
        + case when val5 is null then 0 else 1 end
        + case when val6 is null then 0 else 1 end
        + case when val7 is null then 0 else 1 end
        + case when val8 is null then 0 else 1 end
    ) res
from mytable;

Another standard solution is to use union all and aggregation. Assuming that the primary key of your table is id, you would go:

select id, avg(val) res
from (
    select id, val1 val from mytable
    union all select id, val2 val from mytable
    union all select id, val3 val from mytable
    union all select id, val4 val from mytable
    union all select id, val5 val from mytable
    union all select id, val6 val from mytable
    union all select id, val7 val from mytable
    union all select id, val8 val from mytable
) t
group by id

The second solution actually highlights the fact that you should fix your data model to store the values in rows, not in columns.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.