MySQL中LIMIT必须置于最外层SELECT末尾,仅支持LIMIT row_count或LIMIT offset, row_count两种语法,且需配合ORDER BY保证结果确定性;分页应避免大OFFSET,改用游标分页;UPDATE/DELETE中LIMIT仅为安全限制,不可替代WHERE;SQL_CALC_FOUND_ROWS已弃用。

MySQL中LIMIT必须放在SELECT末尾,不能当函数用
直接报错 ERROR 1064 的常见原因,就是把 LIMIT 当成函数或独立语句:比如写成 LIMIT(10)、LIMIT 10 单独一行、或者插在 WHERE 前面。它只是 SELECT 的一个子句,必须紧跟在 ORDER BY(如果有)之后、语句结尾之前。
只支持两种合法形式:
-
LIMIT row_count:取前 N 行,等价于LIMIT 0, row_count -
LIMIT offset, row_count:跳过 offset 行后取 row_count 行
注意:offset 和 row_count 必须是**非负整数常量**,不能是变量、表达式或函数调用(如 LIMIT @page * 10, 10 会报错)。如果要动态计算,得在应用层拼接或用预处理语句传参。
分页时OFFSET越大越慢,不是LIMIT的问题,是执行逻辑决定的
查第 1000 页(每页 20 条)对应 LIMIT 19980, 20,MySQL 仍要扫描前 19980 行——哪怕不返回它们。这不是 bug,而是 InnoDB 扫描索引+回表的必然过程。
真实线上场景中,OFFSET > 10000 就该警惕了。优化方向不是“调优 LIMIT”,而是换策略:
- 用主键/时间戳做游标分页:比如上一页最后一条是
id = 54321,下一页就写WHERE id > 54321 ORDER BY id LIMIT 20 - 前端限制最大可翻页码(如禁止超过 200 页),或改用“加载更多”滚动加载
- 确保排序字段有索引;若按
created_at分页,建联合索引(created_at, id)而非单列索引
游标分页不支持跳页,但性能稳定;传统 LIMIT offset, size 支持跳页,代价是深度分页时延迟飙升。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
ORDER BY 不加,LIMIT 返回结果可能每次都不一样
InnoDB 使用 MVCC,没有显式排序时,行物理顺序受插入、删除、并发更新影响。即使表数据没变,两次执行 SELECT * FROM users LIMIT 10 也可能返回不同 10 行。
尤其在分页场景下,这会导致用户看到重复或漏掉数据。所以只要用了 LIMIT,就必须搭配 ORDER BY:
- 优先用主键排序(如
ORDER BY id),避免因业务字段重复导致排序不稳定 - 如果按
price排序,而多条记录 price 相同,应补上主键作为第二排序条件:ORDER BY price DESC, id DESC - 不要依赖
ORDER BY RAND()做分页——它无法利用索引,大表会全表扫描
MySQL 8.0+ 的 LIMIT ... OFFSET 写法更清晰,但和传统写法不完全等价
LIMIT 10 OFFSET 20 和 LIMIT 20, 10 语义相同,但前者可读性更好,也符合 SQL 标准。不过要注意:
-
OFFSET后面的值仍是**跳过的行数**,不是页码,别写成LIMIT 10 OFFSET 3想取第 3 页 - 老版本 MySQL(OFFSET 关键字,必须用逗号语法
- 某些 ORM 或中间件(如 MyBatis、ShardingSphere)对
OFFSET语法兼容性较差,上线前务必验证
真正容易被忽略的是:无论用哪种写法,只要涉及分页,就得同步考虑排序稳定性、索引覆盖、以及深度偏移带来的性能断崖——这些不是 LIMIT 自身能解决的,得从查询设计层面动手。










