python 如何有效地获得结果集的计数?

9cbw7uwe  于 2023-01-12  发布在  Python
关注(0)|答案(2)|浏览(129)

我得到了一个与postgres数据库交互的函数。
该函数接受一个名为pagination_data_required(布尔值)的参数。
如果pagination_required被设置为true,函数将执行一个查询和一个query. count(),根据这里的文档docs.peewee链接,query. count()将查询放入一个 Package 的count()函数中。

def list_records(pagination_data_required):
       query = table1.select(table1.columns...).join(table2....).distinct() ## returns nearly 500k rows
if (filter_request_body.pagination_data_required):
       total_count = query.count()

当调用. count()时,我的问题就出现了,如果没有. count(),我的api会在一秒内返回结果,而如果有. count(),响应时间会飙升到18秒。
由于前端团队的要求,我需要此总数。
该查询返回大约50万条记录(这是必需的,另外还调用了. paginate()函数)
如何有效地计算query中返回的行数?
我已经尝试了pagination_data_required True和False的api,结果仍然相同。
我尝试过在原始查询上调用. dicts()并计算条目数,但它给出的响应时间是相同的。

mrphzbgm

mrphzbgm1#

计算查询返回的行数的唯一方法是执行查询并计算结果。我不知道您的ORM如何实现分页,但我假设它会在查询的末尾附加一个LIMIT子句。这样可以加快执行速度。因为只需要计算结果集的前几行,但是对于较大的结果集,计算计数将花费更长的时间。
因此,除了 * 不 * 显示准确的结果计数之外,没有其他好的解决方案。请参阅我的文章,了解对该问题的讨论和潜在的解决方法。

xzv2uavs

xzv2uavs2#

这是经典之作。
通常不可能在不运行查询的情况下计算查询返回的行数。如果它包含不改变计数的内容,如左连接、排序、不添加或删除行的外键连接等,那么您可以删除它们并获得一些加速。但您仍将运行查询。但如果它使用受限索引扫描来高效搜索最近的行(例如)那么这种优化就不会对计数起作用。而且读取如此大量的无用数据会破坏你的缓存。如果计数查询经常运行,它使用的所有数据都会填满你的缓存,并驱逐其他更有用的查询所需要的数据,这将使这些查询变慢。2或者你将不得不升级你的内存。
在某些情况下,比如一个论坛,显示一个主题总是使用相同的搜索条件。它只是"where topic_id =... order by post_id"。在这种情况下,统计帖子是非常浪费的,总是从头做一遍完全相同的查询,和分页结果(LIMIT + OFFSET)也很慢,因为它会丢弃所请求的偏移量之前的所有选定行。由于最常请求的页是最后一页,最坏的情况是最常见的。
但是,有了这样固定的搜索和排序条件,结果集中任何一行的行号总是相同的,所以可以在posts表中缓存为"post_number in topic",那么,要想得到某个特定的页面,就只是"post_number BETWEEN ... AND ..."的问题,而要统计一个主题中的帖子,只要选择最后一个的post_number。2在这种情况下,不需要实际计数就可以得到准确的计数,并且不使用OFFSET就可以分页,这样会快很多。
对于可以使用许多标准的一般搜索查询,不可能以如此简单的方式存储行号。但是,通常不需要知道确切的计数。当GUI显示以下内容时:
页码:1 2 3 4 ... 50000 50001
用户会导航到第837页吗?可能不会。在这种情况下,用户所做的是使用sort将他们想要的结果放在最上面,或者细化他们的搜索条件以将结果的数量减少到可管理的数量。因此,花费在这个巨大的count()查询上的时间几乎总是被浪费掉了。基本上,与用户相关的信息是:是因为页数少,所以可以用眼睛浏览,还是因为页数多,所以他应该细化搜索标准?
这不需要准确的计数,因此解决此问题的最简单方法是将计数结果限制为能够填充5或10页的数量。

SELECT count(*) FROM ...

use:

SELECT count(*) FROM (subquery ORDER BY ... LIMIT ...) AS foo

下一步是要意识到选择几页结果通常几乎和选择一页一样快,所以这是一个很好的机会,当第一页被请求时,至少缓存前几页的结果。这允许在检索到比需要的更多的结果时消除计数。
也可以将前几个页面返回给客户端,并在客户端使用JavaScript分页,这意味着端查询。
用户经常会点击最后一页而不是反转顺序,在这种情况下,您应该反转ORDER BY方向以保持一个小的LIMIT,不计算所有行,并使用一个大的OFFSET跳过除最后一页之外的所有页。当根据请求的页面使用正确的ORDER BY方向时,最常见的是(第一页和最后一页)是最快的,最坏的情况是在中间,很少被点击。
另一种选择是缓存计数。最大的计数很可能出现在涉及很少搜索条件的查询中,这些查询可能具有共同的值,这导致可以预先缓存的一些组合。此外,如果用户单击第2页,则重用上一页的缓存计数。当然,计数不会精确。但这并不重要。只有当分页逻辑出错时才有关系,即没有为接近最后一个请求的页面翻转ORDER BY。
由于前端团队的要求,我需要此总数。
这是不可能的,所以前端团队需要阅读您的问题的答案并采取相应的行动。

相关问题