Python Django查询集上的Count vs len

Python Django查询集上的Count vs len,python,django,performance,Python,Django,Performance,在Django中,假设我有一个QuerySet,我将迭代并打印结果,那么计算对象的最佳选项是什么len(qs)或qs.count() (同时考虑到在同一迭代中计算对象不是一个选项。)尽管建议使用count而不是len: 注意:如果您只想确定集合中的记录数,请不要在查询集上使用len()。使用SQL的SELECT count(*),在数据库级别处理计数要高效得多,Django正是出于这个原因提供了count()方法 由于您无论如何都在迭代此查询集(除非您正在使用),因此最好使用len,因为这样可以

在Django中,假设我有一个
QuerySet
,我将迭代并打印结果,那么计算对象的最佳选项是什么
len(qs)
qs.count()

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

尽管建议使用
count
而不是
len

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

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

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

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

另外,
qs.count()
将给出返回整数,您无法对其进行迭代

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

让我为您提供几个场景:

  • (最重要的)当您只想知道元素的数量,而不打算以任何方式处理它们时,使用
    count()
  • DO:
    queryset.count()
    -这将执行单个
    选择count(*)某些表的查询,所有计算都在RDBMS端进行,Python只需检索结果编号,固定成本为O(1)

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

  • 当您打算无论如何获取queryset时,最好使用
    len()
    ,这不会像
    count()
    那样导致额外的数据库查询
  • len()
    (一个数据库查询)

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

  • 还原的第二种情况(当已获取queryset时):

  • 只要看一眼“引擎盖下”,一切都会变得清晰:

    Django文档中的良好参考:


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

    如果我们有一个简单的人物模型和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()
    快几乎2x

    总结其他人已经回答的问题:

    • len()
      将获取所有记录并对其进行迭代
    • count()
      将执行SQL计数操作(处理大型查询集时速度要快得多)
    同样正确的是,如果在这个操作之后,整个查询集将被迭代,那么作为一个整体,使用
    len()
    可能会稍微有效一些

    但是

    在某些情况下,例如当内存有限时,可以方便地(在可能的情况下)分割在记录上执行的操作。 这可以通过使用


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

    有趣的问题。我建议对此进行分析。。我会很感兴趣的!我对python的了解还不够,不知道完全求值对象上的len()是否有很多开销。它可能比数数还快!回答得很好,+1用于在上下文中发布
    QuerySet
    实现。这确实是一个完美的答案。解释使用什么,更重要的是,解释使用的原因。
     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)
    
    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)