在 Django 中,考虑到我QuerySet
要迭代并打印结果,那么计算对象的最佳选择是什么?len(qs)
还是qs.count()
?
(同时考虑到在同一迭代中计算对象不是一种选择。)
在 Django 中,考虑到我QuerySet
要迭代并打印结果,那么计算对象的最佳选择是什么?len(qs)
还是qs.count()
?
(同时考虑到在同一迭代中计算对象不是一种选择。)
尽管Django 文档建议使用count
而不是len
:
len()
注意:如果您只想确定集合中的记录数,请不要在 QuerySets 上使用。使用 SQL 在数据库级别处理计数要高效得多SELECT COUNT(*)
,而 Djangocount()
正是出于这个原因提供了一种方法。
由于您无论如何都在迭代此 QuerySet,结果将被缓存(除非您正在使用iterator
),因此最好使用len
,因为这样可以避免再次访问数据库,也可以避免检索不同数量的结果!) .
如果您正在使用iterator
,那么出于同样的原因,我建议您在迭代(而不是使用 count)时包含一个计数变量。
len()
在两者之间进行选择count()
取决于情况,值得深入了解它们如何正确使用它们。
让我为您提供几个场景:
count()
:DO: queryset.count()
- 这将执行单个SELECT COUNT(*) FROM some_table
查询,所有计算都在 RDBMS 端进行,Python 只需要以 O(1) 的固定成本检索结果编号
不要: len(queryset)
- 这将执行SELECT * FROM some_table
查询,获取整个表 O(N) 并需要额外的 O(N) 内存来存储它。这是可以做的最糟糕的事情
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
恢复第二种情况(当已经获取查询集时):
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 文档中的良好参考:
我认为len(qs)
在这里使用更有意义,因为您需要迭代结果。qs.count()
如果您想要做的所有事情都打印计数而不是迭代结果,这是一个更好的选择。
len(qs)
将使用 击中数据库,select * from table
而使用qs.count()
击中数据库select count(*) from table
。
也会qs.count()
给出返回整数,你不能迭代它
对于喜欢测试测量(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()
总结其他人已经回答的内容:
len()
将获取所有记录并对其进行迭代。count()
将执行 SQL COUNT 操作(在处理大查询集时要快得多)。确实,如果在此操作之后,将迭代整个查询集,那么作为一个整体,使用len()
.
然而
在某些情况下,例如当有内存限制时,拆分对记录执行的操作可能很方便(如果可能)。这可以使用django pagination来实现。
然后,使用count()
将是选择,您可以避免一次获取整个查询集。