在Django中,假设我有一个QuerySet
我要迭代并打印结果,那么计算对象的最佳选择是什么? len(qs)
或qs.count()
?
(同样假设在同一次迭代中计算对象不是一种选择。)
答案 0 :(得分:98)
虽然Django docs建议使用count
而不是len
:
注意:如果您只想确定集合中的记录数,请不要在QuerySet上使用
len()
。使用SQL的SELECT COUNT(*)
处理数据库级别的计数效率更高,而Django正是因为这个原因提供count()
方法。
由于您正在迭代此QuerySet,the result will be cached(除非您使用iterator
),因此最好使用len
,因为这样可以避免命中数据库再次,也可能检索不同数量的结果!)。
如果您使用的是iterator
,那么我会建议您在迭代时使用计数变量(而不是使用计数),原因相同。
答案 1 :(得分:31)
在len()
和count()
之间选择取决于具体情况,深入了解他们如何正确使用它们是值得的。
让我为您提供一些方案:
(最关键)如果您只想知道元素的数量并且不打算以任何方式处理它们,那么使用count()
至关重要:
DO: queryset.count()
- 这将执行单SELECT COUNT(*) some_table
查询,所有计算都在RDBMS端进行,Python只需要以固定成本O检索结果编号(1)
请勿: len(queryset)
- 这将执行SELECT * FROM some_table
查询,获取整个表O(N)并需要额外的O(N)内存来存储它。 这是最糟糕的事情
无论如何,当您打算获取查询集时,使用len()
稍微好一些,这不会导致count()
的额外数据库查询:
len(queryset) # 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
计数:
queryset.count() # this will perform an extra db query - len() did not
for obj in queryset: # fetching data
pass
还原第二种情况(已提取查询集时):
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文档中的好引用:
答案 2 :(得分:24)
我认为使用len(qs)
更有意义,因为您需要迭代结果。 qs.count()
是一个更好的选择,如果你想要做的只是打印计数而不是迭代结果。
len(qs)
将使用select * from table
命中数据库,而qs.count()
将使用select count(*) from table
命中数据库。
同样qs.count()
将给出返回整数,你不能迭代它
答案 3 :(得分:1)
对于更喜欢测试测量的人(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()
快len()
快{strong> 2倍。
答案 4 :(得分:1)
总结其他人已经回答的问题:
stroke
将获取所有记录并对其进行迭代。gradient
将执行SQL COUNT操作(处理大型查询集时要快得多)。如果在执行此操作之后,整个查询集将被迭代,那么从整体上来说,使用len()
的效率可能会更高。
但是
在某些情况下,例如,在有内存限制的情况下,可以方便地(在可能的情况下)拆分对记录执行的操作。 可以使用django pagination来实现。
然后,使用count()
是一种选择,您可以避免必须一次获取整个查询集。