PostgreSQL:按name列去重并保留最高ts_rank值的实现方案
解决按name去重并保留最高rank的问题
嘿,我懂你的需求啦——现在你的查询会返回多个相同name的结果,你想要每个name只留下对应rank最高的那一行对吧?这在PostgreSQL里有两种实用的实现方式,我给你详细说说:
方法一:用PostgreSQL专属的DISTINCT ON(简洁高效)
DISTINCT ON是PostgreSQL的语法糖,能快速实现「按指定列去重,保留每组排序后第一行」的需求,特别适配你的场景:
SELECT DISTINCT ON (name) name, ts_rank(to_tsvector(name), query) + ts_rank(to_tsvector(content), query2) AS rank FROM users INNER JOIN microposts ON users.id = microposts.user_id, plainto_tsquery('re') query, plainto_tsquery('comics') query2 WHERE users.name @@ query OR microposts.content @@ query2 ORDER BY name, rank DESC;
重点说明:
DISTINCT ON (name):明确指定按name列去重,每组仅保留第一行数据ORDER BY name, rank DESC:必须先按去重列排序,再按rank降序排列——这样每组里rank最高的行会排在第一位,刚好被DISTINCT ON保留下来
方法二:用窗口函数ROW_NUMBER()(通用兼容型)
如果你需要兼容其他数据库,或者后续有更复杂的分组逻辑,窗口函数是更通用的选择:
WITH ranked_results AS ( SELECT name, ts_rank(to_tsvector(name), query) + ts_rank(to_tsvector(content), query2) AS rank, ROW_NUMBER() OVER (PARTITION BY name ORDER BY rank DESC) AS rn FROM users INNER JOIN microposts ON users.id = microposts.user_id, plainto_tsquery('re') query, plainto_tsquery('comics') query2 WHERE users.name @@ query OR microposts.content @@ query2 ) SELECT name, rank FROM ranked_results WHERE rn = 1 ORDER BY rank DESC;
重点说明:
PARTITION BY name:按name分组,给每组内的行单独编号ORDER BY rank DESC:每组内按rank降序编号,最高rank的行编号为1- 最后筛选
rn = 1的行,就得到了每个name对应的最高rank记录
两种方法都能满足你的需求,DISTINCT ON在PostgreSQL里性能更优、写法更简洁;窗口函数则适配性更强,适合复杂场景。你可以根据自己的实际情况选择~
内容的提问来源于stack exchange,提问作者Lee Eather
相关产品推荐
相关产品推荐

