LIMIT必须放在SELECT语句末尾,严格位于ORDER BY和GROUP BY之后、WHERE之后;不可单独使用或插入中间,否则报ERROR 1064。

LIMIT必须放在SELECT末尾,不能当函数用
直接写LIMIT(10)、LIMIT 10单独一行、或插在WHERE前面,都会触发ERROR 1064。它不是函数,也不是独立语句,只是SELECT的子句,必须紧贴在ORDER BY(如果有)之后、语句结尾之前。
-
LIMIT row_count:取前 N 行,等价于LIMIT 0, row_count -
LIMIT offset, row_count:跳过 offset 行后取 row_count 行,offset 从 0 开始计数(第 3 页、每页 10 条 →LIMIT 20, 10,不是LIMIT 30, 10) - 两个参数都必须是非负整数常量,不能是变量、表达式或函数调用(如
LIMIT @page * 10, 10会报错);动态值需在应用层拼接,或用预处理语句传参
不加ORDER BY时,LIMIT返回结果每次都不一样
InnoDB 使用 MVCC,无显式排序时,行物理顺序受插入、删除、并发更新影响。两次执行SELECT * FROM users LIMIT 5可能返回完全不同的 5 条记录——这不是 bug,是未定义行为。
- 分页场景下,这会导致用户看到重复或漏掉数据
- 优先用主键排序:
ORDER BY id;若按业务字段(如price)排序且存在重复值,补上主键作为第二条件:ORDER BY price DESC, id DESC - 别依赖
ORDER BY RAND()做分页,它无法走索引,大表必全表扫描
LIMIT offset, row_count越往后越慢,不是语法问题,是执行逻辑决定的
MySQL 不会“跳”到第 100000 行,而是先按ORDER BY完整扫描并排序所有匹配行,再逐行计数、丢弃前 offset 行,最后取 row_count 行。OFFSET 超过 10000 就该警惕了。
- 真实线上场景中,
LIMIT 19980, 20(查第 1000 页)仍要读、排序、临时存下至少 20000 行 - 优化方向不是“调优 LIMIT”,而是换策略:用主键/时间戳做游标分页,例如上一页最后一条是
id = 54321,下一页就写WHERE id > 54321 ORDER BY id LIMIT 20 - 前端应限制最大可翻页码,或改用“加载更多”滚动加载;确保排序字段有索引,如按
created_at分页,建联合索引(created_at, id)
子查询和JOIN中用LIMIT要格外小心
MySQL 5.7 及更早版本不支持在FROM子句中直接用LIMIT(如SELECT * FROM (SELECT * FROM t LIMIT 10) AS tmp会报错),8.0+ 支持但仍有边界情况;老版本还报错This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'。
- 绕法:用 CTE 包裹一层(MySQL 8.0+):
WITH top3 AS (SELECT id FROM logs ORDER BY ts DESC LIMIT 3) SELECT * FROM events WHERE log_id IN (SELECT id FROM top3) - JOIN 中的
LIMIT作用于整个外层结果,不是对某张表单独限制 - 若业务需要“每组取前 N 条”,
LIMIT完全无能为力,得用ROW_NUMBER()窗口函数或变量模拟,否则极易拿错数据
真正麻烦的从来不是LIMIT怎么写,而是当OFFSET超过十万级后,你才发现它不是“跳过”,而是“硬扫”。这时候看EXPLAIN比改 SQL 更重要——rows飙升、Extra出现Using filesort或Using temporary,就是信号。游标分页虽不能跳页,但它把性能从线性下降拉回常数级,代价只是客户端多记一个值。


















