Di Django, diberikan bahwa saya memiliki QuerySet
yang akan saya ulangi dan mencetak hasilnya, apa pilihan terbaik untuk menghitung objek? len(qs)
atau qs.count()
?
(Juga mengingat bahwa menghitung objek dalam iterasi yang sama bukanlah suatu pilihan.)
python
django
performance
antonagestam
sumber
sumber
Jawaban:
Meskipun dokumen Django merekomendasikan penggunaan
count
daripadalen
:Karena Anda tetap mengulang QuerySet ini, hasilnya akan disimpan dalam cache (kecuali jika Anda menggunakannya
iterator
), dan karenanya akan lebih disukai untuk digunakanlen
, karena ini menghindari memukul database lagi, dan juga kemungkinan mengambil jumlah hasil yang berbeda !) .Jika Anda menggunakan
iterator
, maka saya akan menyarankan untuk memasukkan variabel penghitungan saat Anda mengulang (daripada menggunakan hitungan) untuk alasan yang sama.sumber
Memilih antara
len()
dancount()
bergantung pada situasinya dan sangatlah penting untuk memahami secara mendalam cara kerjanya untuk menggunakannya dengan benar.Izinkan saya memberi Anda beberapa skenario:
(paling penting) Ketika Anda hanya ingin mengetahui jumlah elemen dan Anda tidak berencana untuk memprosesnya dengan cara apa pun, penting untuk menggunakan
count()
:LAKUKAN:
queryset.count()
- ini akan melakukanSELECT COUNT(*) some_table
kueri tunggal , semua komputasi dilakukan di sisi RDBMS, Python hanya perlu mengambil nomor hasil dengan biaya tetap O (1)JANGAN:
len(queryset)
- ini akan melakukanSELECT * FROM some_table
kueri, mengambil seluruh tabel O (N) dan membutuhkan memori O (N) tambahan untuk menyimpannya. Ini adalah hal terburuk yang bisa dilakukanSaat Anda bermaksud untuk mengambil queryset, sebaiknya gunakan
len()
yang tidak akan menyebabkan kueri database tambahan seperticount()
: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
Menghitung:
queryset.count() # this will perform an extra db query - len() did not for obj in queryset: # fetching data pass
Kasus kedua yang dikembalikan (saat queryset telah diambil):
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)
Semuanya akan menjadi jelas setelah Anda melirik "di bawah tenda":
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)
Referensi bagus dalam dokumen Django:
sumber
QuerySet
implementasi secara kontekstual.Saya pikir menggunakan
len(qs)
lebih masuk akal di sini karena Anda perlu mengulang hasil.qs.count()
adalah pilihan yang lebih baik jika semua yang Anda ingin lakukan itu mencetak hitungan dan tidak mengulang hasil.len(qs)
akan masuk ke database denganselect * from table
sedangkanqs.count()
akan menekan db denganselect count(*) from table
.juga
qs.count()
akan memberikan bilangan bulat kembali dan Anda tidak dapat mengulanginyasumber
Untuk orang yang lebih suka pengukuran tes (Postresql):
Jika kita memiliki model Person sederhana dan 1000 contoh darinya:
class Person(models.Model): name = models.CharField(max_length=100) age = models.SmallIntegerField() def __str__(self): return self.name
Dalam kasus rata-rata itu memberi:
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)
Jadi bagaimana Anda bisa melihat
count()
hampir 2x lebih cepat daripadalen()
dalam kasus pengujian khusus ini.sumber
Meringkas apa yang telah dijawab orang lain:
len()
akan mengambil semua catatan dan mengulanginya.count()
akan melakukan operasi SQL COUNT (jauh lebih cepat saat menangani queryset besar).Juga benar bahwa jika setelah operasi ini, seluruh queryset akan diulangi, maka secara keseluruhan itu bisa sedikit lebih efisien untuk digunakan
len()
.Namun
Dalam beberapa kasus, misalnya ketika memiliki keterbatasan memori, akan lebih mudah (jika memungkinkan) untuk membagi operasi yang dilakukan pada record. Itu bisa dicapai dengan menggunakan pagination django .
Kemudian, menggunakan
count()
akan menjadi pilihan dan Anda bisa menghindari harus mengambil seluruh queryset sekaligus.sumber