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

Postgres结合GORM实现基于ID的键集分页与Title去重查询

实现PostgreSQL中唯一Title的键集分页(GORM+原生SQL方案)

核心思路

  1. 保证Title唯一:如果表中未给title加唯一约束,需通过分组聚合(取每个title对应的最小id)得到去重后的id-title集合;若已有唯一约束,可直接跳过此步。
  2. 键集分页实现:利用id的唯一性与有序性,用WHERE id > 上一页最大id替代传统OFFSET,避免大偏移量导致的性能损耗,同时保证分页结果的稳定性(数据插入/删除时不会出现重复或遗漏)。

GORM实现方案

1. 定义数据结构

如果不需要映射完整表结构,可定义专用返回结构体:

type UniqueTitleItem struct {
    ID    uint   `gorm:"column:id"`
    Title string `gorm:"column:title"`
}

2. 第一页查询

通过CTE(公共表表达式)先获取去重后的数据集,再分页:

var page1 []UniqueTitleItem
err := db.
    With("unique_titles", db.Table("articles").Select("MIN(id) as id, title").Group("title")).
    Table("unique_titles").
    Order("id ASC").
    Limit(3).
    Scan(&page1).Error
if err != nil {
    // 处理错误逻辑
}
// 记录当前页最大ID,用于下一页查询
lastID := page1[len(page1)-1].ID

3. 后续页查询

基于上一页的最大id作为查询条件:

var page2 []UniqueTitleItem
err := db.
    With("unique_titles", db.Table("articles").Select("MIN(id) as id, title").Group("title")).
    Table("unique_titles").
    Where("id > ?", lastID).
    Order("id ASC").
    Limit(3).
    Scan(&page2).Error

若表中title已添加唯一约束,可简化为直接查询,无需CTE分组:

// 第一页
var page1 []Article
db.Select("id, title").Order("id ASC").Limit(3).Find(&page1)
// 后续页
var page2 []Article
db.Select("id, title").Where("id > ?", lastID).Order("id ASC").Limit(3).Find(&page2)

原生SQL方案

第一页SQL

WITH unique_titles AS (
    SELECT MIN(id) AS id, title
    FROM articles
    GROUP BY title
)
SELECT id, title
FROM unique_titles
ORDER BY id ASC
LIMIT 3;

第二页SQL(假设上一页最大ID为4)

WITH unique_titles AS (
    SELECT MIN(id) AS id, title
    FROM articles
    GROUP BY title
)
SELECT id, title
FROM unique_titles
WHERE id > 4
ORDER BY id ASC
LIMIT 3;

注意事项

  • 确保id字段有索引(主键默认带索引),否则键集分页的性能优势无法体现。
  • 若需按title排序后分页,需保证排序字段的唯一性(可结合id作为排序条件,如ORDER BY title ASC, id ASC),避免分页结果出现重复或遗漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:52:59