MySQL数据-实现分页的最佳方法?


208

我的iPhone应用程序连接到我的PHP Web服务,以从MySQL数据库检索数据。一个请求可以返回500个结果。

一次实现分页并检索20个项目的最佳方法是什么?

假设我从数据库中收到了前20个广告。现在如何请求接下来的20个广告?

Answers:


309

从MySQL文档

LIMIT子句可用于约束SELECT语句返回的行数。LIMIT接受一个或两个数字参数,这两个参数都必须是非负整数常量(使用预处理语句时除外)。

有两个参数,第一个参数指定要返回的第一行的偏移量,第二个参数指定要返回的最大行数。初始行的偏移量是0(不是1):

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

要检索从某个偏移量到结果集结尾的所有行,可以为第二个参数使用较大的数字。该语句检索从第96行到最后一行的所有行:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

使用一个参数,该值指定从结果集的开头返回的行数:

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

换句话说,LIMIT row_count等效于LIMIT 0 row_count。


107
使用LIMIT进行分页时,还应指定ORDER BY。
Mark Byers

10
@shylent:引用文档没有错,但是我同意他应该提到他正在复制文档并提供了指向原始资源的链接。同样令我感到惊讶的是,文档中将包含使用LIMIT而不使用ORDER BY ...的示例,这似乎令人鼓舞。如果没有ORDER BY,则不能保证两次调用之间的顺序相同。
Mark Byers

13
无论如何,在对大结果集进行分页时(这就是分页的目的-将大结果集分成较小的块,对吗?),请记住,如果执行a limit X, Y,本质上会发生的是检索X + Y行,然后删除从开头开始的X行,并返回剩下的所有内容。重申:limit X, Y导致扫描X + Y行。
shylent 2010年

7
我不喜欢您的LIMIT 95,18446744073709551615想法。.看一看OFFSET;-)
CharlesLeaf 2010年

5
在处理大数据时这效率不高。检查codular.com/implementing-pagination,了解适合特定场景的多种方式。
阿米特(Amit)

125

对于500条记录,效率可能不是问题,但是如果您有数百万条记录,那么使用WHERE子句选择下一页可能是有利的:

SELECT *
FROM yourtable
WHERE id > 234374
ORDER BY id
LIMIT 20

这里的“ 234374”是您上次查看的页面中最后一条记录的ID。

这将使id上的索引可用于查找第一条记录。如果您使用它,LIMIT offset, 20您会发现随着向最后翻页,它变得越来越慢。就像我说的,只有200条记录可能并不重要,但是对于较大的结果集却可以有所作为。

这种方法的另一个优点是,如果两次调用之间的数据发生变化,您将不会错过任何记录或获得重复的记录。这是因为添加或删除行意味着更改后所有行的偏移量。对于您而言,这可能并不重要-我猜您的广告库不会经常更改,而且无论如何,没人会注意到他们是否连续两次获得相同的广告-但是,如果您正在寻找“最佳方式”那么选择使用哪种方法时,还要记住另一件事。

如果您确实希望使用带偏移量的LIMIT(如果用户直接导航到第10000页而不是一页一页地浏览,则必须这样做),那么您可以阅读这篇有关后期行查找的文章,以提高LIMIT的性能。抵消。


1
这更像是:P尽管我完全不同意这种暗示,但“较新的” ids总是比“较旧的” ids大,大多数情况下确实如此,因此,我认为这是“好”足够'。无论如何,是的,正如您所演示的那样,正确的分页(在大型结果集上不会造成严重的性能下降)并不是特别琐碎的limit 1000000, 10事情,并且编写并希望它能起作用将不会带您到任何地方。
shylent

1
后期查找链接非常有用
pvgoddijn 2015年

1
如果仅使用“ DESC”进行ID排序,则此分页将向后工作。我喜欢!
丹尼斯·海登

2
但是人们在现实世界中要多久按ID或Insinuation或“创建日期”进行订购?
RichieHH

好的帖子,但area=width*height不仅仅是重要的记录数量,而且每条记录的大小也是将结果存储到内存中的一个因素
没必要,2017年

43

为查询定义偏移。例如

第1页-(记录01-10):偏移量= 0,限制= 10;

第2页-(记录11-20)偏移= 10,限制= 10;

并使用以下查询:

SELECT column FROM table LIMIT {someLimit} OFFSET {someOffset};

第2页的示例:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
您不是要为第2页的offset = 10吗?
珍娜·梅兹

28

有关于它的文献:

主要问题发生在使用大OFFSETs时。他们避免使用OFFSET带有各种技术,从id范围选择在WHERE子句中的到某种缓存或预计算页面。

Use INDEX,Luke中有建议的解决方案:


1
对于复杂查询的每个分页查询,获取最大ID会导致不切实际,并且非生产性使用确实会影响页面的行数,行号和子句类型之间的性能!
Rizwan Patel

在提供的链接中已考虑并适当评估了该策略。根本不是那么简单。
Luchostein '17

提供的链接似乎只能满足基本枢轴单枢轴,交叉应用,多CTE或派生表机制的要求?我再次坚持我的观点,再次重写如此庞大的查询,以求获得最高的认可是架构上的过大!然后再次对排列顺序为n“的列进行置换和组合!
Rizwan Patel

1
我是不是误解了“分页方式正确”链接,还是在涉及过滤的任何查询中根本不切实际。
contactmatt

1
@contactmatt我分享您的见识。最后,似乎没有办法有效地实现全部要求,但是围绕原始要求放宽了变化。
Luchostein


6

你也可以

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

select语句的行计数(没有限制)在同一条select语句中捕获,因此您无需再次查询表大小。您可以使用SELECT FOUND_ROWS()获得行数;


1
这是特别低效的。将*在更多的列比需要被提取,结果SQL_CALC_FOUND_ROWS这些列结果被读取的所有表中的行,即使它们不包含在结果中。在不读取所有这些列的单独查询中计算行数会更加有效。然后,您的主查询可以在读取20行后停止。
thomasrutter

你确定吗?我针对大型表SQL_CALC_FOUND_ROWS和另一个未使用的查询计时查询。我没看到时差。任何方法都比执行2个查询快。1-从最大限制0 20中选择*,然后从最大限制中选择count(*)。
surajz 2012年

1
是的,我确定- 这是更多信息。在所有情况下,当您使用索引来筛选行时,SQL_CALC_FOUND_ROWS的速度明显慢于执行两个单独的查询的速度。在极少数情况下,您不使用索引,或者(如在此简化示例中)您没有WHERE子句,而它是一个MYISAM表,它几乎没有什么区别(速度差不多)。
thomasrutter

而且,这里是一个关于它的讨论#1
thomasrutter

4

查询1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

查询2: SELECT * FROM tbl LIMIT 0,500;

查询1的记录较小或中等时运行速度更快,如果记录数等于5,000或更高,则结果类似。

500条记录的结果:

Query1花费9.9999904632568毫秒

Query2花费19.999980926514毫秒

8,000条记录的结果:

Query1需要129.99987602234毫秒

Query2需要160.00008583069毫秒


您需要在上添加一个索引id
Maarten 2015年

6
id > 0什么用?
Michel Jung 2015年

1
就像Maarten所说的那样,这两个查询在本质上看起来是相同的,并且可能以任何一种方式分解为相同的机器级命令。您必须遇到索引问题或MySQL的真正旧版本。
HoldOffHunger '16

谢谢,因为在我没有看到您的答案的情况下,我只需要查看顺序,顺序和限制的顺序
Shreyan Mehta

使用了错误的示例。使用offset(limit的第一个参数是offset),您仍在选择所有数据到limit,然后丢弃该数量的offset,然后返回介于offset和之间的部分limitwhere另一方面,with 子句将为查询设置一种起点,并查询ONLY该特定部分。
senaps

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.