108

在 Django 中,考虑到我QuerySet要迭代并打印结果,那么计算对象的最佳选择是什么?len(qs)还是qs.count()

(同时考虑到在同一迭代中计算对象不是一种选择。)

4

5 回答 5

148

尽管Django 文档建议使用count而不是len

len()注意:如果您只想确定集合中的记录数,请不要在 QuerySets 上使用。使用 SQL 在数据库级别处理计数要高效得多SELECT COUNT(*),而 Djangocount()正是出于这个原因提供了一种方法。

由于您无论如何都在迭代此 QuerySet,结果将被缓存(除非您正在使用iterator),因此最好使用len,因为这样可以避免再次访问数据库,也可以避免检索不同数量的结果!) .
如果您正在使用iterator,那么出于同样的原因,我建议您在迭代(而不是使用 count)时包含一个计数变量。

于 2013-01-14T21:32:31.380 回答
88

len()在两者之间进行选择count()取决于情况,值得深入了解它们如何正确使用它们。

让我为您提供几个场景:

  1. (最重要的)当您只想知道元素的数量并且不打算以任何方式处理它们时,使用count()

DO: queryset.count() - 这将执行单个SELECT COUNT(*) FROM some_table查询,所有计算都在 RDBMS 端进行,Python 只需要以 O(1) 的固定成本检索结果编号

不要: len(queryset) - 这将执行SELECT * FROM some_table查询,获取整个表 O(N) 并需要额外的 O(N) 内存来存储它。这是可以做的最糟糕的事情

  1. 无论如何,当您打算获取查询集时,最好使用它len()不会导致额外的数据库查询count()

len()(一个数据库查询)

    len(queryset) # SELECT * fetching all the data - NO extra cost - data would be fetched anyway in the for loop

    for obj in queryset: # data is already fetched by len() - using cache
        pass

count()(两个数据库查询!):

    queryset.count() # First db query SELECT COUNT(*)

    for obj in queryset: # Second db query (fetching data) SELECT *
        pass
  1. 恢复第二种情况(当已经获取查询集时):

     for obj in queryset: # iteration fetches the data
         len(queryset) # using already cached data - O(1) no extra cost
         queryset.count() # using cache - O(1) no extra db query
    
     len(queryset) # the same O(1)
     queryset.count() # the same: no query, O(1)
    

一旦你“在引擎盖下”看一眼,一切都会变得清晰:

class QuerySet(object):
   
    def __init__(self, model=None, query=None, using=None, hints=None):
        # (...)
        self._result_cache = None
 
    def __len__(self):
        self._fetch_all()
        return len(self._result_cache)
 
    def _fetch_all(self):
        if self._result_cache is None:
            self._result_cache = list(self.iterator())
        if self._prefetch_related_lookups and not self._prefetch_done:
            self._prefetch_related_objects()
 
    def count(self):
        if self._result_cache is not None:
            return len(self._result_cache)
 
        return self.query.get_count(using=self.db)

Django 文档中的良好参考:

于 2017-10-07T11:02:43.673 回答
29

我认为len(qs)在这里使用更有意义,因为您需要迭代结果。qs.count()如果您想要做的所有事情都打印计数而不是迭代结果,这是一个更好的选择。

len(qs)将使用 击中数据库,select * from table而使用qs.count()击中数据库select count(*) from table

也会qs.count()给出返回整数,你不能迭代它

于 2013-01-14T21:46:13.863 回答
5

对于喜欢测试测量(Postresql)的人:

如果我们有一个简单的 Person 模型和它的 1000 个实例:

class Person(models.Model):
    name = models.CharField(max_length=100)
    age = models.SmallIntegerField()

    def __str__(self):
        return self.name

在平均情况下,它给出:

In [1]: persons = Person.objects.all()

In [2]: %timeit len(persons)                                                                                                                                                          
325 ns ± 3.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit persons.count()                                                                                                                                                       
170 ns ± 0.572 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

那么你怎么能比这个特定的测试用例count()2倍呢?len()

于 2019-11-01T14:34:07.907 回答
1

总结其他人已经回答的内容:

  • len()将获取所有记录并对其进行迭代。
  • count()将执行 SQL COUNT 操作(在处理大查询集时要快得多)。

确实,如果在此操作之后,将迭代整个查询集,那么作为一个整体,使用len().

然而

在某些情况下,例如当有内存限制时,拆分对记录执行的操作可能很方便(如果可能)。这可以使用django pagination来实现。

然后,使用count()将是选择,您可以避免一次获取整个查询集。

于 2020-03-20T20:52:26.550 回答