
本文介绍在处理超大规模数据(如10亿级唯一ID)时,如何使用DuckDB的arg_max等高性能聚合函数,在仅4GB内存约束下,精准、低开销地为每个id选取ornum最大的记录。
本文介绍在处理超大规模数据(如10亿级唯一id)时,如何使用duckdb的`arg_max`等高性能聚合函数,在仅4gb内存约束下,精准、低开销地为每个`id`选取`ornum`最大的记录。
当面对数十亿级别唯一 id 的海量表(如 source 表含 id: LONG, name: STRING, ornum: LONG),传统基于窗口函数(如 ROW_NUMBER() OVER (PARTITION BY id ORDER BY ornum DESC))的方案会强制全量排序并缓存中间结果,极易触发磁盘溢出或OOM——尤其在仅4GB内存的环境中,该方法实际不可行。
DuckDB 提供了专为“分组取极值行”场景优化的聚合函数 arg_max() 和 arg_min(),它们无需显式排序整个数据集,而是在线扫描中维护每组的最优候选,内存占用与唯一 id 数量呈线性关系(而非总行数),完美适配大数据量、高基数分组场景。
✅ 推荐方案(高效、省内存):
CREATE OR REPLACE TABLE target AS SELECT id, arg_max(name, ornum) AS name, max(ornum) AS ornum FROM source GROUP BY id;
✅
arg_max(col, weight)返回weight最大时对应的col值;支持多列聚合(如需返回整行,可组合结构体或struct_pack,见下文)。
⚠️ 若需完整原始行(不止 name 和 ornum),可借助 struct_pack 构造行结构后取极值:
CREATE OR REPLACE TABLE target AS SELECT id, (arg_max(struct_pack(x := name, y := ornum), ornum)).* FROM source GROUP BY id;
该写法将 name 和 ornum 打包为结构体,以 ornum 为权重取最大,再解构(.*)还原为独立列,语义清晰且仍保持内存友好。
❌ 不推荐方案(高内存风险):
-- ❌ 避免!需全局排序 + 窗口计算,内存/IO开销巨大 SELECT * FROM source QUALIFY row_number() OVER (PARTITION BY id ORDER BY ornum DESC) = 1;
即使 DuckDB 支持此语法,其执行计划仍需构建按 id 分区、按 ornum 排序的完整中间状态,对10亿 id 场景极易耗尽内存或严重依赖临时磁盘,性能下降数个数量级。
? 关键总结:
-
arg_max()/arg_min()是 DuckDB 针对“分组取顶行”问题的原生优化方案,时间复杂度接近 O(N),空间复杂度 ≈ O(唯一键数 × 平均行宽); - 优先使用聚合函数替代窗口函数,尤其在内存受限、高基数分组场景;
- 测试时建议开启
PRAGMA memory_limit='4GB';模拟真实约束,并用EXPLAIN ANALYZE验证执行计划是否避免ORDER BY和WINDOW AGGREGATE节点。

















