You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中高效实现先排序再去重(取各id最小时间行)

高性能获取每个ID最小时间对应的记录

先明确咱们的原始数据表结构和数据:

id | time | value
1 | 0:00 | 'a'
1 | 2:00 | 'b'
2 | 4:00 | 'c'
2 | 3:00 | 'd'
2 | 2:00 | 'e'
3 | 5:00 | 'f'
3 | 3:00 | 'g'

需求很清晰:给每个id挑出唯一一条记录,而且这条记录得是该id下time最小的那条,同时要适配百万级数据量,必须高性能,不能搞那种先子查询排序再去重的冗余操作。

方案1:聚合关联查询(通用SQL,适配绝大多数数据库)

这是最通用的高效路子,先通过聚合拿到每个id的最小time,再和原表关联拿到完整记录,全程没全局排序,性能拉满,尤其适合给表建了(id, time)联合索引的场景:

SELECT t.*
FROM your_table t
INNER JOIN (
    SELECT id, MIN(time) AS min_time
    FROM your_table
    GROUP BY id
) AS agg ON t.id = agg.id AND t.time = agg.min_time;

必加的性能buff:

  • 给表建联合索引(id, time):数据库能直接通过索引快速算出每个id的最小time,不用扫全表;关联的时候也能直接定位到目标行,速度快得飞起。
  • 别碰全局排序:这种方式只做一次聚合+一次关联,计算量比先排序再去重小太多,百万级数据下差距特别明显。

方案2:DISTINCT ON(PostgreSQL专属)

如果你用的是PostgreSQL,那这个语法简直是为这个需求量身定做的,简洁又高效:

SELECT DISTINCT ON (id) *
FROM your_table
ORDER BY id, time ASC;

性能优化关键点:

  • 同样要建联合索引(id, time):数据库会直接用索引按id分组,然后取每组的第一条(也就是time最小的那条),完全不用额外排序,性能拉满。

方案3:窗口函数(适合复杂筛选场景)

如果你的需求后续还要加更复杂的筛选逻辑,窗口函数也是个不错的选择,但要注意不要做多余计算:

SELECT id, time, value
FROM (
    SELECT *,
           ROW_NUMBER() OVER (PARTITION BY id ORDER BY time ASC) AS rn
    FROM your_table
) AS t
WHERE rn = 1;

性能注意事项:

  • 必须有(id, time)联合索引:数据库可以利用索引来做分区和排序,避免全表排序。相比那种先全局排序再去重的方案,窗口函数的分区排序是局部的,性能要好很多,但比前两种方案稍逊一筹。

为啥要避开“先排序再去重”?

百万级数据量下,全局排序(比如先ORDER BY再用GROUP BY或DISTINCT)会产生巨量的IO和计算开销,甚至可能触发磁盘排序,直接把性能拉垮。而上面的几个方案都是基于分组聚合或者索引直接定位目标行,计算量和IO开销都小得多,完全适配百万级数据的场景。

内容的提问来源于stack exchange,提问作者user1299412

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:40:11