如何获取超过1000个数据?

52

如何从数据存储中获取超过1000条记录,并将所有记录放入单个列表中以传递给Django?

16个回答

38
自版本1.3.6(发布于2010年8月17日)起,您可以执行数据存储计数()查询的结果和所有数据存储查询的偏移量不再被限制在1000个以下。

从变更日志中:


2
我仍然只得到了1000,而应该是约3600。如何实现这一点?谢谢。 - Ivan Slaughter
5
@Ivan 一次请求只能返回1000条结果,但你可以使用迭代来查询每次获取1000条结果,并将偏移量设置为下一个1000。我会很快发布相关代码。 - Shay Erlichmen
6
例如,以下代码可以正常运行:numTotalUsers = User.all(keys_only=True).count(999999) # 其中 999999 是最大限制。否则计数结果将为 1000。 - jpswain
使用偏移量实际上不推荐,因为会影响性能和成本。您应该使用游标,如此处所示: https://developers.google.com/appengine/docs/python/datastore/queries#Python_Query_cursors - Erwan
这并不是推荐的做法。你应该考虑使用分片计数器:https://blog.svpino.com/2015/03/08/how-to-count-all-entries-of-a-given-type-in-the-app-engine-datastore - svpino

23

仅记录一下 - 现在已经取消了最多1000个条目的获取限制:

http://googleappengine.blogspot.com/2010/02/app-engine-sdk-131-including-major.html

引用:

不再有1000个结果限制 - 是的:随着游标的添加以及过去几个月中许多更小的数据存储稳定性和性能改进的总结,我们现在足够有信心完全删除最大结果限制。无论您是进行获取、迭代还是使用游标,都没有结果数量的限制。


19

App Engine提供了一种很好的“分页”方式,通过按键排序并使用最后一个键作为下一个偏移量,每次可以获取1000个结果。他们甚至在此处提供了一些示例代码:

http://code.google.com/appengine/docs/python/datastore/queriesandindexes.html#Queries_on_Keys

虽然他们的示例将查询分散到许多请求中,但您可以将页面大小从20更改为1000,并在循环中查询,结合查询集。此外,您可能会使用itertools连接查询,而不是在需要之前对它们进行评估。

例如,要计算1000以后有多少行:

class MyModel(db.Expando):
    @classmethod
    def count_all(cls):
        """
        Count *all* of the rows (without maxing out at 1000)
        """
        count = 0
        query = cls.all().order('__key__')

        while count % 1000 == 0:
            current_count = query.count()
            if current_count == 0:
                break

            count += current_count

            if current_count == 1000:
                last_key = query.fetch(1, 999)[0].key()
                query = query.filter('__key__ > ', last_key)

        return count

2
如果实际计数恰好是1000的倍数,这将永远循环——错误的退出条件!-) 否则很好... - Alex Martelli
4
无法运行。由于计数器被初始化为零,while循环永远不会被执行。 - dave paola

18
每当这个问题被提出作为一个限制时,我总是想知道“为什么需要超过1000个结果?”你知道吗,Google自己也不提供超过1000个结果的服务吗?试试这个搜索:http://www.google.ca/search?hl=en&client=firefox-a&rls=org.mozilla:en-US:official&hs=qhu&q=1000+results&start=1000&sa=N我直到最近才知道这一点,因为我从未花时间点击查询结果的第100页。

如果您实际上将超过1000个结果返回给用户,那么我认为存在比数据存储不允许这样做更大的问题。

需要返回这么多的结果可能有一个(合法的)原因,即如果您正在对数据进行大量操作并呈现摘要(例如所有这些数据的平均值是多少)。这个问题的解决办法(在Google I/O talk中有讨论)是随着数据的到来实时计算摘要数据并保存它们。


1
同意。在单个页面中向用户返回数千个结果是没有意义的。 - Nick Johnson
从那里可以得出结论,如果你不打算将所有记录返回给用户,那么从数据存储中检索1000条记录是没有意义的。 - Tony Arkles
1
如果我想对存储在数据存储中的1000多个实体的属性求和,我需要以某种方式解决这个限制。jgeewax提供了我正在寻找的解决方案。 - bentford

14

你不能。

FAQ的一部分说明,无法访问查询结果中超过1000行的数据,增加“OFFSET”只会导致结果集更短,

例如:OFFSET 999 --> 只返回1个结果。

来自维基百科:

App Engine将从每个Datastore调用返回的实体获取限制为最多1000行。大多数Web数据库应用程序使用分页和缓存,因此不需要一次获取这么多数据,因此在大多数情况下,这不是问题。[需要引证]如果应用程序需要每个操作超过1,000条记录,则可以使用其自己的客户端软件或Ajax页面对无限数量的行执行操作。

来自 http://code.google.com/appengine/docs/whatisgoogleappengine.html

另一个服务限制的例子是查询返回的结果数。查询最多可以返回1,000个结果。将返回更多结果的查询仅返回最大结果。在这种情况下,执行此类查询的请求不太可能在超时之前返回请求,但是该限制是为了节省数据存储上的资源。

来自 http://code.google.com/appengine/docs/datastore/gqlreference.html

注意:LIMIT子句最多为1000。如果指定的限制大于最大值,则使用最大值。这个最大值也适用于GqlQuery类的fetch()方法。
注意:与fetch()方法的偏移参数一样,GQL查询字符串中的OFFSET不会减少从数据存储中获取的实体数量。它只影响由fetch()方法返回的结果。具有偏移量的查询具有与偏移量大小成正比的性能特征。
来自http://code.google.com/appengine/docs/datastore/queryclass.html 限制和偏移参数控制从数据存储中获取多少结果以及fetch()方法返回多少结果:
- 数据存储将offset + limit个结果提供给应用程序。数据存储本身不跳过前面的offset个结果。 - fetch()方法跳过前面的offset个结果,然后返回剩下的limit个结果。 - 查询的性能特征与偏移量加上限制成正比。
这意味着,如果您有一个单一的查询,则无法请求超出范围0-1000之外的任何内容。
增加偏移量只会增加0,因此...
LIMIT 1000  OFFSET 0    

将返回1000行,

LIMIT 1000 OFFSET 1000 

将返回0行,因此无法使用单个查询语法手动或使用API获取2000个结果。

唯一可能的例外

是在表上创建数值索引,即:

 SELECT * FROM Foo  WHERE ID > 0 AND ID < 1000 

 SELECT * FROM Foo WHERE ID >= 1000 AND ID < 2000

如果您的数据或查询不能有这个“ID”硬编码标识符,那么您就没有好运气了。


这是我知道的。但是我该如何每次获取1000个,并创建一个包含2000个的列表? - Zote
列表1 = 第一个查询 列表2 = 第二个查询 列表1 += 列表2 - Tom Leys
Tom:如果第二个查询由于数据库限制保证返回0行,那么它就是无意义的。 - Kent Fredric
6
请注意,这个回答现在有点过时:现在可以使用__key__伪属性进行排序和过滤,这允许您对任意大的结果集进行逐片迭代。 - Nick Johnson
可以使用jgeewax的方法。 - bentford

10

这个1K限制问题已经解决。

query = MyModel.all()
for doc in query:
    print doc.title

通过将 Query 对象视为可迭代的对象:迭代器以小批量从数据存储库中检索结果,使应用程序可以停止对结果进行迭代,以避免获取不必要的内容。当检索到与查询匹配的所有结果时,迭代停止。与 fetch() 一样,迭代器接口不会缓存结果,因此从 Query 对象创建新的迭代器将重新执行查询。

最大批处理大小为1K,同时仍具有自动 Datastore 配额。

但是,在计划1.3.1 SDK中,他们引入了可以序列化和保存的游标,以便未来调用可以从它上次停止的位置开始查询。


7

6

当记录超过1000条时,通过远程API获取数据仍然存在问题。我们编写了这个小函数来分块迭代表中的数据:

def _iterate_table(table, chunk_size = 200):
    offset = 0
    while True:
        results = table.all().order('__key__').fetch(chunk_size+1, offset = offset)
        if not results:
            break
        for result in results[:chunk_size]:
            yield result
        if len(results) < chunk_size+1:
            break
        offset += chunk_size

3
我们在我们的ModelBase类中使用了一些东西,它是:
@classmethod
def get_all(cls):
  q = cls.all()
  holder = q.fetch(1000)
  result = holder
  while len(holder) == 1000:
    holder = q.with_cursor(q.cursor()).fetch(1000)
    result += holder
  return result

这种方法可以规避每个模型的1000次查询限制,而无需考虑其他事情。我想实现一个键版本也同样容易。


2
entities = []
for entity in Entity.all():
    entities.append(entity)

简单来说,每个实体都会进行一次RPC,这比分批获取要慢得多。因此,如果您关心性能,请按照以下方式操作:
如果您的项目少于1M个条目:
entities = Entity.all().fetch(999999)

否则,请使用光标。
还应该注意以下几点:
Entity.all().fetch(Entity.all().count())

返回最大值1000,不建议使用。


1
那么,如果您遍历Entity.all()查询,即使它是#100,000,您仍将继续获得结果,直到达到与查询匹配的最后一项?当您在#999,#1999,#2999时,GAE是否准备好下一批? - David Haddad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接