PostgreSQL按天分组获取最大val值及对应name的查询实现
问题根因
- PostgreSQL 严格遵循SQL标准的聚合校验规则:当使用
GROUP BY完成分组聚合时,SELECT子句中出现的列,要么是纳入GROUP BY的分组列,要么是被聚合函数(如max()、sum())包裹的列,否则会直接抛出语法错误。 - 如果强行将
name字段加入GROUP BY的分组条件,分组粒度会从“按单日聚合”变为“按单日+单name聚合”,最终返回的是每个天、每个name各自的val最大值,完全偏离统计单日全局最大值的需求。 - 普通分组聚合的逻辑只能返回分组的统计值本身,无法自动带出该统计值对应的关联行字段,因此需要换用其他查询逻辑实现需求。
可落地的实现方案
方案1:窗口函数实现(PostgreSQL原生支持,执行效率高)
核心思路:按天对数据做分区,在每个单日的分区内将val按从高到低排序,直接取每个分区排序首位的整行数据,即可同时获取day、val、name三个字段。
SELECT day, name, val FROM ( SELECT day, name, val, -- 按天分区,val降序排序 ROW_NUMBER() OVER (PARTITION BY day ORDER BY val DESC) AS sort_rank FROM 你的实际表名 -- 替换为真实表名即可 ) ranked_data WHERE sort_rank = 1;
边界场景说明:如果同一天存在多个并列的最大值(比如示例中12-01-2021的Een和Twee的val均为1,都是当日最大值),
ROW_NUMBER()会强制为并列值分配不同序号,仅返回其中一行。如果需要保留所有并列最大值的记录,将ROW_NUMBER()替换为RANK()即可。
方案2:聚合结果关联原表实现
核心思路:先通过原有的分组逻辑计算出每日的val最大值,再将聚合结果和原表做内连接,匹配同日期、val等于当日最大值的行,拿到对应的name字段。
SELECT t.day, t.name, t.val FROM 你的实际表名 t INNER JOIN ( -- 先算出每日的max val SELECT day, max(val) AS daily_max_val FROM 你的实际表名 GROUP BY day ) max_table ON t.day = max_table.day AND t.val = max_table.daily_max_val;
特点说明:这种写法会保留所有和当日最大值匹配的行,不会出现并列值被随机截断的问题,更适合需要完整保留并列统计结果的场景。
以你提供的示例数据为例,最终查询会返回两行结果:11-01-2021对应val=19、name=Drie;12-01-2021对应val=1,匹配到name为Een和Twee的记录,完全匹配需求。
注意:如果当前表的
day字段是MM-DD-YYYY格式的字符串,建议先将其转换为DATE类型再做分区、排序和关联,避免字符串排序逻辑导致的数值匹配偏差。
内容的提问来源于stack exchange,提问作者twit
相关产品推荐
相关产品推荐

