如何基于指定列筛选ActiveRecord的前n条唯一记录?
解决ActiveRecord按指定列去重并获取前n条完整对象的问题
我懂你的需求啦——你想基于特定列(比如name)去重,只保留每个唯一值对应的第一条记录,然后取前n条完整的Recommendation对象,而不是只拿到字段值对吧?之前用limit或者单纯的distinct确实达不到效果,因为前者不管重复直接取前n条,后者搭配pluck只能拿字段,没法返回完整模型实例。
这里给你两个靠谱的解决方案,适配不同场景:
方案一:通用窗口函数法(支持所有主流数据库)
利用窗口函数实现精准分组去重,同时保留完整对象:
# 第一步:构建子查询,给每个name分组的记录标记行号 ranked_recommendations = Recommendation.select( "recommendations.*", # 按name分组,组内按id升序排序,给每条记录标记行号 "ROW_NUMBER() OVER (PARTITION BY name ORDER BY id ASC) AS row_num" ).where(id: [1,2,3,4]) # 你的初始筛选条件 # 第二步:从子查询中筛选每个分组的第一条记录,再取前2条 unique_top_recommendations = Recommendation.from(ranked_recommendations, :recommendations) .where(row_num: 1) .limit(2)
原理说明
ROW_NUMBER() OVER (PARTITION BY name ORDER BY id ASC) 会把相同name的记录归为一组,每组内按id从小到大排序,给第一条记录标记row_num=1,后续同name的记录行号递增。我们只筛选row_num=1的记录,就得到了每个唯一name对应的第一条记录,再用limit(2)就能拿到你要的结果(比如记录1和记录3,而非重复的记录1和2)。
方案二:PostgreSQL专属简化法(Rails 7+)
如果你用的是PostgreSQL数据库,且Rails版本在7及以上,可以用distinct_on语法简化写法:
Recommendation.where(id: [1,2,3,4]) .distinct_on(:name) .order(:name, :id) .limit(2)
这个方法会按name去重,自动保留每个name组内按id排序的第一条记录,同样能返回完整的模型实例,语法更简洁,但仅适用于PostgreSQL环境。
内容的提问来源于stack exchange,提问作者user2954587
相关产品推荐
相关产品推荐

