[python] Django QuerySet의 개수 대 len

장고에서 QuerySet반복하고 결과를 인쇄 할 것이 있다는 점을 감안할 때 개체를 계산하는 가장 좋은 옵션은 무엇입니까? len(qs)또는 qs.count()?

(또한 동일한 반복에서 객체를 계산하는 것은 옵션이 아닙니다.)



답변

하지만 장고 워드 프로세서 사용을 권장 count하기보다는 len:

참고 : len()집합의 레코드 수를 확인하기 만하면되는 경우 QuerySets에서 사용하지 마십시오 . SQL을 사용하여 데이터베이스 수준에서 카운트를 처리하는 것이 훨씬 더 효율적 SELECT COUNT(*)이며 Django는 count()정확하게 이러한 이유로 메서드를 제공합니다 .

어쨌든이 검색어 세트를 반복하고 있기 때문에, 결과는 캐시됩니다 (당신이 사용하지 않는 경우 iterator), 그리고 그것을 사용하는 것이 바람직 할 수 있도록 len하기 때문에, 이 피합니다이 가능 결과 다른 번호를 검색 또한 데이터베이스를 다시 타격, 그리고 !) .
을 사용하는 경우 iterator동일한 이유로 (count를 사용하는 대신) 반복 할 때 계수 변수를 포함하는 것이 좋습니다.


답변

len()및 중 선택 count()하는 것은 상황에 따라 다르며 올바르게 사용하기 위해 작동하는 방식을 깊이 이해하는 것이 좋습니다.

몇 가지 시나리오를 제공하겠습니다.

  1. (가장 중요) 요소 수만 알고 싶고 어떤 식 으로든 처리 할 계획이없는 경우 count()다음 을 사용하는 것이 중요합니다 .

    DO : queryset.count() -이것은 단일 SELECT COUNT(*) some_table쿼리 를 수행 합니다. 모든 계산은 RDBMS 측에서 수행됩니다. Python은 고정 비용이 O (1) 인 결과 번호를 검색하면됩니다.

    하지 말아야 할 사항 : len(queryset)SELECT * FROM some_table쿼리 를 수행하고 전체 테이블 O (N)을 가져오고이를 저장하기 위해 추가 O (N) 메모리가 필요합니다. 할 수있는 최악의 상황

  2. 어쨌든 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
    
  3. 두 번째 경우 되돌림 (쿼리 세트를 이미 가져온 경우) :

    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()을 선택하면 전체 쿼리 세트를 한 번에 가져 오지 않아도됩니다.


답변