分页查到第二页,出现了第一页已经看过的记录,翻回去一对比,两页有重复。原因是查询只写了 limit,没写 order by,数据库返回的顺序本来就不保证。

SQL order by 放在 where 之后、limit 之前,默认升序 asc,倒序加 desc。多列排序用逗号隔开,前一个字段相同才看后一个。排序字段可以是列名、别名或者表达式,select 里起的别名在 order by 里多数数据库都认。字段名后面跟数字表示按第几列排,但可读性差,线上代码别这么写。

SELECT id, name, price FROM goods WHERE status = 1 ORDER BY price DESC, id ASC LIMIT 20 OFFSET 0;

排序字段有重复值,翻页就不稳

价格相同的商品谁在前谁在后,数据库没有义务保持一致。分页要稳定,就在末尾补一个唯一字段,通常加主键 id。这样每页的边界固定,不会漏数据也不会重复。只按时间排序最容易出这个问题,同一秒下单的记录顺序是随机的。

NULL 和中文排序各有各的规则

MySQL 里升序时 null 默认排在最前,想控制顺序可以写 order by 字段 is null, 字段。中文按拼音还是笔画,取决于字段的排序规则,utf8mb4_general_ci 和 utf8mb4_0900_ai_ci 的结果可能不同。线上和本地排序不一致,先查两边的字符集设置。

大表排序慢在哪

order by 的字段有索引,多数情况能直接走索引顺序;没有索引就要临时排序,数据量大时会写磁盘。explain 里看到 Using filesort 不一定是错,但 rows 很大就要注意。只按主键排序成本最低,业务能接受就优先用。对字段做函数运算会让索引失效,比如 order by date(create_time) 基本都要临时排序,能改成范围条件就改。

写分页查询的习惯:where 条件、order by 唯一键、limit 三件套配齐。少一个,线上就可能出现翻页重复这种看起来莫名其妙的问题。offset 很大时翻页还会变慢,可以改成记录上一页末尾那条记录的 id,用 where id > 值 往下取。