2016-11-11 85 views
1

我有一个REST服务,它根据当前页面和每页结果返回数据库表中的行。mySQL - 过滤行的分页

当未过滤的结果,这是很容易做到的,我只是做一个SELECT WHERE ID> =(页 - 1)* perPage + 1和LIMIT到perPage。

问题是当试图对过滤结果使用分页时,如果我选择仅筛选WHERE type = someType的行。

在这种情况下,第一页的第一个匹配可以在id 7开始,最后一个可以在id 5046中。然后第二个页面的第一个匹配可以从7302开始并在12430结束,以此类推。

对于过滤结果的第一页,我可以简单地从id 1开始,LIMIT到perPage,但对于第二页等,我需要知道上一个匹配行的索引页面,甚至更好 - 当前页面中第一个匹配的行或其他指示。

我该如何有效地做到这一点?我需要能够在具有数百万行的表上执行此操作,因此显然读取所有行并从中取出并不是一个选项。

的想法是这样的:

SELECT ... FROM ... WHERE filterKey = filterValue AND id >= id_of_first_match_in_current_page

与id_of_first_match_in_current_page是其中的奥秘。

+1

这似乎是一个非常奇怪的方式来做到这一点。在我的经验研究小型/中型数据集标准是为了通过行,这样就得到一个一致的数据集(这样你就可以使用任何WHERE),然后使用LIMIT和,位我怀疑你可能已经错过了,偏移子句告诉MySQL从位置X返回行。 –

+0

过滤的数据集通常可能有多大? – Strawberry

+0

乔恩 - 我明白了,我不熟悉OFFSET,谢谢。草莓 - 我希望能够一次获取200行的东西,从过滤的结果可以达到成千上万。 –

回答

2

您无法知道给定页面上的第一个ID是什么,因为ID号码不一定是顺序的。换句话说,序列中可能存在空白,所以100行的第五页上的行不一定从id 500开始。它可以以id 527开始,例如,不可能知道。

换言之另一种方式:ID是一个值,而不是行号。

如果你的客户是通过以升序页推进一个可行的办法是,每个REST请求获取数据,指出值页面上的最大 ID,然后使用该在下一个 REST请求,以便它查询id值较大。

SELECT ... FROM ... WHERE filterKey = filterValue 
AND id > id_of_last_match_of_previous_page 

但是,如果您的REST请求可以获取任意随机页面,则此解决方案不起作用。这取决于已经获取了先前的页面。

另一种解决方案是使用LIMIT <x> OFFSET <y>语法。这使您可以请求任何任意页面。 LIMIT <y>, <x>的工作原理是相同的,但出于某种原因,x和y在两种不同的语法形式中是相反的,所以请牢记这一点。

使用LIMIT...OFFSET在请求结果中包含多页的页面时效率不高。假设您要求第5,000页。 MySQL必须在5,000页的服务器端生成一个结果,然后丢弃其中的4,999个,并返回结果中的最后一页。对不起,但这是它的工作原理。


回复您的评论:

你必须明白,WHERE适用于行的条件,但网页是由行位置定义。这是确定行的两种不同方式!

如果您有一列保证为行号,那么您可以像使用行位置那样使用该值。您甚至可以在其上放置索引,或将其用作主键。

但是,主键值可能会更改,并且可能不连续,例如,如果更新或删除行或回滚某些事务等等。对主键值重新编号是一个坏主意,因为其他表或外部数据可能会引用主键值。

所以你可以添加另一列而不是的主键,但只有一个行号。

ALTER TABLE MyTable ADD COLUMN row_number BIGINT UNSIGNED, ADD KEY (row_number); 

然后在需要对行重新编号时填写值。

SET @row := 0; 
UPDATE MyTable SET row_number = (@row := @row + 1) ORDER BY id; 

例如,如果您曾删除某些行,您将不得不重新编号行。经常这样做效率不高,取决于表格的大小。

另外,如果不锁定表格,新插入无法创建正确的行号值。这对于防止竞争条件是必要的。

如果您保证row_number是一系列连续值,那么它既是一个值又是一个行位置,因此您可以将它用于任何任意行的页面的高性能索引查找。

SELECT * FROM MyTable WHERE row_number BETWEEN 401 AND 500; 

至少直到下一次通过删除或新插入对行号序列置疑。

+0

感谢您的详细解答。有没有一种有效的方法可以让我请求任意的页面?现在,随着大数据的所有进步,人们必须想到一些事情。 –

+0

重新编辑 - 很好的答案。非常感谢你。 –

1

您正在使用ID列作错误的用途。 ID是记录的标识符,而不是任何给定结果集的记录的序列号

LIMIT关键字延伸到基本分页。如果你只是想前10条记录,你会做这样的事情:

LIMIT 10 

进行分页,如果你想要的 10条记录,你会怎么做:

LIMIT 10,10 

的10之后:

LIMIT 20,10 

依此类推。

LIMIT的子句是独立于WHERE子句。使用WHERE来筛选结果,使用LIMIT分页。