PostgreSQL中替代distinct on实现高效查询的方法
问题
我的查询运行耗时过长,通过执行计划分析发现“unique”节点占用大量计算时间。目前我用窗口函数max(grade) over (partition by class)计算每个班级的最高成绩,再通过distinct on (class)来保留每个班级的结果。
示例数据:
| 班级 | 学生 | 成绩 |
|---|---|---|
| 1 | A | 0 |
| 1 | B | 10 |
| 1 | C | 11 |
| 1 | D | 7 |
| 2 | E | 20 |
| 2 | F | 18 |
| 2 | G | 19 |
需求是获取每个班级的最高成绩:
| 班级 | 最高成绩 |
|---|---|
| 1 | 11 |
| 2 | 20 |
当前使用的SQL语句:
select distinct on (class) class, max_grade from (select max(grade) over (partition by class) as max_grade, student, class) t
请问是否有比distinct on更优的方式实现该结果?
优化方案
直接用分组聚合是更高效的实现方式,完全不需要窗口函数和distinct on,能彻底避免unique节点带来的额外开销:
select class, max(grade) as 最高成绩 from your_table_name group by class
方案优势
- 分组聚合是这类“按组求极值”需求的原生解决方案,PostgreSQL会直接对
class分组后计算最大值,执行计划更简洁,省去了先为每一行生成窗口函数结果再去重的步骤。 - 对比原方案,减少了子查询的内存占用,也规避了
distinct on触发的排序、去重操作(也就是你观察到的unique节点耗时)。 - 如果
class字段上建有索引,分组聚合的性能还能进一步提升,PostgreSQL可以利用索引快速完成分组计算。
注:原SQL的子查询缺失表名和别名,即使补全后,性能也远不如直接分组聚合。
内容的提问来源于stack exchange,提问作者cobdmg
相关产品推荐
相关产品推荐

