SELECT
mpr.id,
mpr.close_time,
mpr.project_start_time
FROM
merchant_project_record mpr
ORDER BY
mpr.close_time < NOW(),
IF(mpr.close_time < NOW(), 0, mpr.close_time),
mpr.close_time DESC
LIMIT 0,10;
1. 排序逻辑分析
mpr.close_time < NOW():返回一个布尔值(0 或 1),表示时间是否已经过期。
IF(mpr.close_time < NOW(), 0, mpr.close_time):如果时间已过期,用 0 替代;否则保留原始时间。
最终排序规则:
第一优先级:是否过期(未过期在前,过期在后)。
第二优先级:未过期的时间越早排在前面(因为 mpr.close_time DESC 是降序)。
第三优先级:mpr.close_time 降序。
2. 分页重复的原因
当使用 LIMIT offset, size 进行分页时,如果多条记录的排序字段完全相同(比如多个记录的 close_time 相同且都未过期),数据库无法保证这些记录的顺序稳定性,从而可能导致上一页最后一条记录与下一页第一条记录重复。
解决方案
✅ 方法一:增加唯一排序字段
为了确保排序唯一性,建议在 ORDER BY 中加入唯一字段(如主键 id)作为最后一级排序条件:
SELECT mpr.id, mpr.close_time
FROM merchant_project_record mpr
ORDER BY
mpr.close_time < NOW(),
IF(mpr.close_time < NOW(), 0, mpr.close_time),
mpr.close_time DESC,
mpr.id DESC -- 增加主键作为最终排序依据
LIMIT 0,10;
这样可以确保即使其他排序字段相同,也能通过 id 确保每条记录的唯一位置。
✅ 方法二:使用游标分页(Cursor-based Pagination)
如果你对性能要求较高,推荐使用 游标分页,避免偏移量带来的性能和重复问题。
假设上一页最后一条记录的 close_time = '2025-04-01 00:00:00', id = 100,那么下一页查询可以写为:
SELECT mpr.id, mpr.close_time
FROM merchant_project_record mpr
WHERE
(mpr.close_time < NOW()) = 0 AND mpr.close_time >= '2025-04-01 00:00:00' AND mpr.id < 100
OR
(mpr.close_time < NOW()) = 0 AND mpr.close_time > '2025-04-01 00:00:00'
ORDER BY
mpr.close_time < NOW(),
IF(mpr.close_time < NOW(), 0, mpr.close_time),
mpr.close_time DESC,
mpr.id DESC
LIMIT 10;
这种方式可以避免传统分页中因排序不稳定导致的数据重复或丢失。
总结