我有一个员工表,其中包含4种类型的奖金。我想将每位员工的四项奖金加起来并得出平均值。有些员工没有资格获得奖金,因此这种奖金的类型不应计入其平均值。除了取笑数据库设计(这不是我的错)之外,有人在计算该平均值时是否有一种创造性的方法来忽略NULL值?
下表中唯一具有正确平均值的员工是Employee1。
SELECT title, (IFNULL(level1,0)+IFNULL(level2,0)+IFNULL(level3,0)+IFNULL(level4,0))/4 as avglevel, level1, level2, level3, level4
FROM test;
Title avglevel level1 level2 level3 level4
Employee1 2500.0000 1000 2000 3000 4000
Employee2 2250.0000 NULL 2000 3000 4000
Employee3 2000.0000 1000 NULL 3000 4000
Employee4 1750.0000 1000 2000 NULL 4000
Employee5 1500.0000 1000 2000 3000 NULL
我找到了此项目,但实际上它仅涵盖AVG函数,并且不除以列数,除非这些列之一为NULL时除外。 Strange MySQL AVG() anomaly NULL values
答案 0 :(得分:2)
由于当前的表格设计,您需要对列(而不是行)执行汇总操作。一般而言,MySQL对跨行/记录的聚合提供了强大的支持,但对列的支持则较少。如果您打算更改表设计,那么AVG
函数已经可以解决此问题,因为它是开箱即用的。考虑将表存储为:
Title | level | amount
Employee2 | 1 | NULL
Employee2 | 2 | 2000
Employee2 | 3 | 3000
Employee2 | 4 | 4000
也就是说,将每个员工的每个级别值存储在单独的记录中。然后,您可以使用以下方法轻松计算正确的平均值:
SELECT
title,
AVG(amount) AS avglevel
FROM test
GROUP BY
title;
默认情况下,AVG
函数将已经忽略NULL
值,因此,总和将由不包括具有NULL
的记录的记录计数标准化。
编辑:
在设置演示之后,您似乎想要使用所有级别归一化平均值,即使金额为NULL
。在这种情况下,我们可以将总和除以计数:
SELECT
title,
SUM(amount) / COUNT(*) AS avglevel
FROM test
GROUP BY
title;
答案 1 :(得分:1)
您可以在使用分母时尝试使用以下情况
SELECT title,
(level1+level2+level3+level4)/(case when level1 is not null then 1 end+
case when level2 is not null then 1 end+case when level3 is not null then 1 end+case when level4 is not null then 1 end) as avglevel,
level1, level2, level3, level4
FROM test
答案 2 :(得分:1)
您可以在单个表达式中执行此操作。 MySQL具有一些内置功能,这些功能使其变得更简单:
SELECT title,
(COALESCE(level1, 0) + COALESCE(level2, 0) + COALESCE(level3, 0) + COALESCE(level4, 0)
) /
( (level1 is not null) + (level2 is not null) +
(level3 is not null) + (level4 is not null)
) as avglevel,
level1, level2, level3, level4
FROM test;
答案 3 :(得分:0)
尝试以下情况,如下所示
SELECT title,
(IFNULL(level1,0)+IFNULL(level2,0)+IFNULL(level3,0)+IFNULL(level4,0))/
(4-(case when level1 is null then 1 else 0 end)-(case when level2 is null then 1 else 0 end)-
(case when level3 is null then 1 else 0 end)- (case when level4 is null then 1 else 0 end) ) as avglevel,
level1, level2, level3, level4
FROM test;