PostgreSQL中如何高效计算去除高低分后的行平均分数?
PostgreSQL 简洁实现去高低分求平均
直接用数组函数结合聚合逻辑就能替代臃肿的UNION ALL写法,而且后续新增score列时只需要修改数组里的列名,非常灵活:
方案一:数组聚合计算法
把所有score列打包成数组,通过拆分行后直接计算总和减去最大最小值,再除以剩余的有效分数数量:
SELECT id, (SUM(s) - MAX(s) - MIN(s)) / (COUNT(s) - 2) AS avg_score FROM scores, unnest(ARRAY[score1, score2, score3, score4, score5, score6, score7]) s GROUP BY id;
这个写法利用unnest将数组拆分为行,再按ID分组计算,逻辑清晰,执行效率远高于UNION ALL的拆分方式。
方案二:窗口函数过滤极值
如果需要严格匹配“各去掉一个最高分和最低分”的逻辑(哪怕存在多个相同极值,也仅各剔除一个),可以用窗口函数标记行号后过滤:
SELECT id, AVG(score) AS avg_score FROM ( SELECT id, score, ROW_NUMBER() OVER (PARTITION BY id ORDER BY score) AS rn_low, ROW_NUMBER() OVER (PARTITION BY id ORDER BY score DESC) AS rn_high FROM scores, unnest(ARRAY[score1, score2, score3, score4, score5, score6, score7]) score ) t WHERE rn_low > 1 AND rn_high > 1 GROUP BY id;
该方案完全贴合你给出的示例逻辑——比如ID3的全7分数据,会去掉一个最高和一个最低分后保留5个7,最终平均仍为7。
适配新增列
后续如果新增score8、score9等列,仅需在数组中补充对应的列名即可,比如ARRAY[score1, score2, ..., score9],无需修改整体查询结构,比UNION ALL的方式省心得多。
处理空值场景
如果score列可能存在NULL值,只需在过滤条件中添加空值判断:
WHERE score IS NOT NULL AND rn_low > 1 AND rn_high > 1
内容的提问来源于stack exchange,提问作者heihei Li
相关产品推荐
相关产品推荐

