'Sum with nulls "not working" in SQL Server / Azure

I'm trying to average a set of columns and exclude rows with zero from the denominator by using a case statement within an AVG() function. I read that avg() excludes NULL results so am using the case statement to replace 0's with NULL's. However that's not what I find in practice - see code below. Can someone explain why this happens? If you can suggest a code tweak to achieve what I'm after that would also be fab. Thanks.

with a as
(
select 0 t1, 3 t2 
)
, b as
(
select 6 t1, 0 t2
)
, c as -- building a small table containing test data.
(
select * from a
union all
select * from b
)
select sum(case when t2 = 0 then null else t2 end + case when t1 = 0 then null else t1 end) r1 
  , avg(case when t2 = 0 then null else t2 end + case when t1 = 0 then null else t1 end) r2
  , avg(t1) r3
from c

What subquery c contains:

t1 t2
0 3
6 0

The actual result of my query:

r1 r2 r3
NULL NULL 3

Column r2 is what I would like the result of my query to be: avg(3 + null, null + 6) = avg(3, 6) = 4.5:

r1 r2 r3
9 4.5 3


Solution 1:[1]

Instead of setting the 0 values to null, you can filter them out somewhere in between:

with a as
(
select cast(0 as float) t1, cast(3 as float) t2 
)
, b as
(
select cast(6 as float) t1, cast(0 as float) t2
)
, c as -- building a small table containing test data.
(
select * from a where t1 > 0 or t2 > 0
union all
select * from b where t1 > 0 or t2 > 0
)
select sum(t2+t1) r1 
  , avg(t2+t1) r2
  , avg(t1) r3
from c;

Output:

| r1 | r2 | r3 |
|----+----+----|
| 9  | 4.5| 3  |

*See how I convert numbers to float type, in order to capture the floating point on r2.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1