工作笔记 - PG分组极值

0 阅读2分钟

概述

笔者在工作的过程中,遇到一个比较典型的问题,就是需要查找分组记录中的某个极值所对应的记录。

一个经典的场景就是学生科目成绩表。分组的依据是科目。想要查询各个科目成绩最好的学生记录(假设没有并列排名)。

比较传统的方式是分组聚合(Group By) ,查到分组中的最高分,然后倒查学生记录。

还有一种方式是使用窗口函数( Rank() over ... ),找到记录在窗口中对应的排序,然后取各个窗口序号为一的那条记录。

笔者觉得这两种方式对于这个简单的需求而言,都过于繁琐。 经过和AI的讨论,发现了一种看起来比较简洁的技术方案。觉得有点意思,处理的思路也很有启发,遂著文记录和分享之。

技术方案

简单而言,这个技术方案的核心SQL是:

"Distinct ON(field)"

它可以处理重复字段值,也就是记录分组,并保留记录其他字段的值。

下面是完整的示例:


SELECT DISTINCT ON (course) stuid, course, score, examtime 
FROM scores 
ORDER BY course, score desc

// 还可以写成

SELECT DISTINCT ON (course) * FROM scores 
ORDER BY course, score desc

假设这里分组的依据是科目 course, 这个查询就可以查出分组中,按照分数倒排第一记录,当然包括学生的ID和其他其他信息。最后再对course进行汇总,就查询出所有科目的第一名学生的信息了。

简单总结一下用法:

  • distinct on () 用于指定分组的依据,所谓分组,就是字段值相同的记录的集合,它使用重复值来进行处理
  • 后面可选要提取的字段,也可以是 *
  • 关键是排序方式,因为这里的场景比较简单,就是取排序中的极值,所以先按分组排序,然后按值排序

这个查询笔者是在Postgres中调试和通过的,其他SQL系统的支持和实现情况未知。

小结

本文探讨和记录了一种不使用聚合查询和窗口函数来查询分组极值记录的一种方式。已经在笔者的一个应用中使用,觉得值得分享和探讨。