programing

장고 쿼리 집합의 카운트() 대 len()

stoneblock 2023. 9. 16. 08:31

장고 쿼리 집합의 카운트() 대 len()

장고에서 제가 지금.QuerySet결과를 반복하여 인쇄할 예정인데 개체 수를 세는 데 가장 적합한 옵션은 무엇입니까?len(qs)아니면qs.count()?

(또한 동일한 반복에서 개체 수를 세는 것은 선택 사항이 아닙니다.)

장고 문서에서는 다음을 사용할 것을 권장합니다.count보다는len:

참고: 사용 안 함len()QuerySets에서 세트의 레코드 수를 결정하는 것만 원하는 경우.SQL을 사용하여 데이터베이스 수준에서 카운트를 처리하는 것이 훨씬 효율적입니다.SELECT COUNT(*), 그리고 장고는 제공합니다.count()바로 이런 이유로 방법을 사용합니다.

어차피 이 QuerySet을 반복하는 이므로 (사용하지 않는 한) 결과가 캐시되므로 사용하는 것이 좋습니다.len, 이렇게 하면 데이터베이스에 다시 타격을 주지 않을 만 아니라 다른 수의 결과를 가져올 가능성도 있기 때문입니다!)
사용하시는 경우iterator, 그런 다음 같은 이유로 카운트를 사용하는 대신 반복할 때 카운트 변수를 포함하는 것을 제안합니다.

둘중에len()그리고.count()상황에 따라 다르며 이를 올바르게 사용하기 위해 어떻게 작동하는지 깊이 이해할 필요가 있습니다.

몇 가지 시나리오를 알려드리겠습니다.

  1. (가장 중요한)요소의 개수만 알고 싶을 뿐 어떤 방식으로도 요소를 처리할 계획이 없는 경우 사용하는 것이 중요합니다.count():

작업관리: queryset.count()- 싱글로 실행됩니다.SELECT COUNT(*) FROM some_table쿼리, 모든 계산은 RDBMS 쪽에서 수행되며, 파이썬은 O(1)의 고정 비용으로 결과 번호만 검색하면 됩니다.

하지 않음: len(queryset)- 이것은 잘 될 것입니다.SELECT * FROM some_table쿼리, 전체 테이블 O(N)을 가져오고 저장을 위해 추가 O(N) 메모리가 필요합니다.이건 최악의 상황입니다.

  1. 어쨌든 쿼리 세트를 가져오려면 사용하는 것이 좋습니다.len()추가적인 데이터베이스 쿼리를 발생시키지 않을 것입니다.count()그럴 것이다

len()(한 db 쿼리)

    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()(두 개의 DB 쿼리!)

    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)

장고 문서의 참고 자료:

제가 생각하기에.len(qs)결과를 반복해야 하므로 여기서 더 합리적입니다.qs.count()원하는 모든 것이 카운트를 인쇄하고 결과를 반복하지 않는 것이 더 나은 옵션입니다.

len(qs)에할다다e할tl에hee를select * from table반면에qs.count()와 함께 db에 도달할 것입니다.select count(*) from table.

.qs.count()하며 이를 할 수 .고할수다환다수를lt할고tret .

테스트 측정(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()보다 거의 2배 빠른 속도가 빠릅니다.len()이 특별한 테스트 케이스에서는

다른 사용자가 이미 답변한 내용 요약:

  • len()모든 기록을 가져와 반복할 겁니다
  • count()SQL COUNT 작업을 수행합니다(큰 쿼리 세트를 처리할 때 훨씬 더 빠릅니다).

가 될 으로 하는 이 일 에 일 이 하는 에 len().

하지만

예를 들어, 메모리 제한이 있는 경우, 레코드를 통해 수행되는 작업을 분할하는 것이 편리할 수 있습니다.그것은 장고 페이지를 이용하여 달성할 수 있습니다.

에 을 count()를 선택할 수 있으므로 전체 쿼리 세트를 한 번에 가져올 필요가 없습니다.

모델 및 원시 쿼리가 카운트()와 len()을 사용하여 1000만 행을 얼마나 빨리 얻을 수 있는지 실험했습니다.*Postgre를 사용하였습니다.SQL 입니다.

<실험 결과>

셈을 보다 len ()
모델조회 1.02초 46초13
원시 쿼리 0.48초 3.16초

따라서 빠른 속도의 순서는 아래와 같습니다.

  1. count()원시 쿼리(0.48초)를 사용합니다.
  2. count()모델 쿼리 포함(1.02초)
  3. len()원시 쿼리(3.16초)를 사용합니다.
  4. len()모델 쿼리 포함(46.13초)

는 기본적으로 합니다를 사용하는 합니다.count()합니다. 모델 는다기델에기다h는tley를n .len(), 원시 쿼리보다 코드가 적고 편리하지만 select_for_update를 사용할 때는 사용해야 합니다.len(). 왜냐하면델와께께와델hhey .select_for_update()와 함께count()작동하지 않으며 원시 쿼리보다 코드가 적고 편리합니다.

<실험방법>

먼저 만 있는 모델을 만들었습니다.

# "store/models.py"

from django.db import models

class Test(models.Model):
    pass

그런 다음 아래 명령을 실행합니다.

python manage.py makemigrations && python manage.py migrate

그런 다음 psql로 테이블에 1000만 을 한 번에 삽입합니다.

postgres=# INSERT INTO store_test (id) SELECT generate_series(1, 10000000);
INSERT 0 10000000
Time: 29929.337 ms (00:29.929)

마지막으로 뛰었습니다.test_view()아래와 같이

# "store/views.py"

from time import time
from .models import Test
from django.db import connection
from django.http import HttpResponse

def test_view(request):

    # "count()" with model query
    start = time()
    print(Test.objects.all().count(), "- count() - Model query")
    end = time()
    print(round(end - start, 2), "seconds\n")

    # "len()" with model query
    start = time()
    print(len(Test.objects.all()), "- len() - Model query")
    end = time()
    print(round(end - start, 2), "seconds\n")
    
    # "count()" with raw query
    start = time()
    with connection.cursor() as cursor:
        cursor.execute("SELECT count(*) FROM store_test;") 
        print(cursor.fetchone()[0], "- count() - Raw query")
    end = time()
    print(round(end - start, 2), "seconds\n")

    # "len()" with raw query
    start = time()
    with connection.cursor() as cursor:
        cursor.execute("SELECT * FROM store_test;")
        print(len(cursor.fetchall()), "- len() - Raw query") 
    end = time()
    print(round(end - start, 2), "seconds\n")
    
    return HttpResponse("Test_view")

콘솔의 출력:

10000000 - count() - Model query
1.02 seconds

10000000 - len() - Model query
46.13 seconds

10000000 - count() - Raw query
0.48 seconds

10000000 - len() - Raw query
3.16 seconds

[18/Dec/2022 07:12:14] "GET /store/test_view/ HTTP/1.1" 200 9

언급URL : https://stackoverflow.com/questions/14327036/count-vs-len-on-a-django-queryset