如何从千万级数据表按uid分组取每组最后2条art_id记录(限200用户/请求)
解决方案
针对你这个1900万行的大表需求——分批处理200个用户、每个用户取最后2条文章记录、结果按uid排序,我整理了一套高效的实现方案,兼顾性能和业务逻辑:
核心SQL(单批处理200用户)
先看单批处理的核心逻辑,用窗口函数给每个用户的记录编号,确保拿到最新的2条,同时限制处理范围:
WITH user_batch AS ( -- 筛选本次要处理的200个用户,可按uid分段避免重复处理 SELECT DISTINCT uid FROM your_table_name -- 这里可以加WHERE uid > [上次处理的最大uid]来分页 ORDER BY uid LIMIT 200 ) SELECT t.id, t.uid, t.art_id FROM ( SELECT id, uid, art_id, -- 按uid分组,组内按id降序编号,rn=1是最新记录 ROW_NUMBER() OVER (PARTITION BY uid ORDER BY id DESC) AS rn FROM your_table_name WHERE uid IN (SELECT uid FROM user_batch) ) t WHERE t.rn <= 2 ORDER BY t.uid, t.id DESC;
关键细节解释
- 窗口函数
ROW_NUMBER():这是实现“每个用户取N条”的标准写法,按uid分组后,用id DESC确保我们拿到的是最新插入的2条记录(如果你的“最后”是指原始插入顺序的末尾,把DESC改成ASC即可)。 - 分批处理逻辑:通过
user_batch这个CTE先锁定200个用户,避免一次性扫描全表,降低数据库负载。如果要迭代处理所有用户,只需要在user_batch里加WHERE uid > [上次处理的最大uid],逐步推进即可。 - 排序要求:最终结果按
uid排序,组内按id降序排列,完全符合你的需求。
大表性能优化必备
1900万行的数据量,必须靠索引提速,否则查询会慢到无法接受。创建这个复合索引:
CREATE INDEX idx_uid_id_desc ON your_table_name (uid, id DESC);
这个索引能让数据库直接定位每个用户的记录,并且已经按id降序排好,窗口函数不需要额外排序,执行效率会提升几个数量级。
用你的示例数据验证
假设你的表名为user_articles,去掉分批限制跑一遍核心逻辑:
SELECT t.id, t.uid, t.art_id FROM ( SELECT id, uid, art_id, ROW_NUMBER() OVER (PARTITION BY uid ORDER BY id DESC) AS rn FROM user_articles ) t WHERE t.rn <= 2 ORDER BY t.uid, t.id DESC;
得到的结果是:
| id | uid | art_id |
|---|---|---|
| 5 | 10 | 16 |
| 3 | 10 | 13 |
| 7 | 11 | 55 |
| 6 | 11 | 36 |
| 8 | 13 | 68 |
这正是每个用户最新的2条记录,如果你需要的是按原始顺序的最后2条(比如uid10取id1、5),把窗口函数里的ORDER BY id DESC改成ORDER BY id ASC就行。
迭代处理全量用户
你可以用脚本(Python/Shell/数据库存储过程)来自动分批处理:
- 初始化
last_processed_uid = 0 - 循环执行:
- 运行带
WHERE uid > last_processed_uid的SQL - 处理返回的结果(导出/写入目标表)
- 更新
last_processed_uid为本次处理的最大uid - 直到查询返回的用户数不足200,结束循环
- 运行带
这样就能高效处理完1900万行数据,不会对数据库造成太大压力。
内容的提问来源于stack exchange,提问作者Tutti Frutti
相关产品推荐
相关产品推荐

