ใน Django เนื่องจากฉันมีสิ่งQuerySet
ที่ฉันกำลังจะทำซ้ำและพิมพ์ผลลัพธ์ตัวเลือกที่ดีที่สุดสำหรับการนับวัตถุคืออะไร? len(qs)
หรือqs.count()
?
(นอกจากนี้เนื่องจากการนับวัตถุในการทำซ้ำเดียวกันไม่ใช่ตัวเลือก)
ใน Django เนื่องจากฉันมีสิ่งQuerySet
ที่ฉันกำลังจะทำซ้ำและพิมพ์ผลลัพธ์ตัวเลือกที่ดีที่สุดสำหรับการนับวัตถุคืออะไร? len(qs)
หรือqs.count()
?
(นอกจากนี้เนื่องจากการนับวัตถุในการทำซ้ำเดียวกันไม่ใช่ตัวเลือก)
คำตอบ:
แม้ว่าเอกสาร Django จะแนะนำให้ใช้count
มากกว่าlen
:
หมายเหตุ: อย่าใช้
len()
กับ QuerySets ถ้าสิ่งที่คุณต้องการทำคือกำหนดจำนวนระเบียนในชุด มีประสิทธิภาพมากกว่าในการจัดการการนับในระดับฐานข้อมูลโดยใช้ SQLSELECT COUNT(*)
และ Django ให้count()
วิธีการด้วยเหตุผลนี้อย่างแม่นยำ
เนื่องจากคุณกำลังทำ QuerySet นี้ซ้ำผลลัพธ์จะถูกแคช (เว้นแต่คุณจะใช้งานอยู่iterator
) ดังนั้นจึงเป็นที่นิยมที่จะใช้len
เนื่องจากวิธีนี้จะหลีกเลี่ยงการกดปุ่มฐานข้อมูลอีกครั้งและอาจดึงผลลัพธ์ที่แตกต่างกันออกไปด้วย !) .
หากคุณกำลังใช้iterator
ฉันขอแนะนำให้รวมตัวแปรการนับในขณะที่คุณทำซ้ำ (แทนที่จะใช้การนับ) ด้วยเหตุผลเดียวกัน
การเลือกระหว่างlen()
และcount()
ขึ้นอยู่กับสถานการณ์และควรทำความเข้าใจอย่างลึกซึ้งถึงวิธีการใช้งานอย่างถูกต้อง
ฉันขอเสนอสถานการณ์บางอย่างให้คุณ:
(สำคัญที่สุด) เมื่อคุณต้องการทราบจำนวนองค์ประกอบเท่านั้นและคุณไม่ได้วางแผนที่จะประมวลผลองค์ประกอบเหล่านี้ แต่อย่างใดสิ่งสำคัญคือต้องใช้count()
:
DO: queryset.count()
- สิ่งนี้จะดำเนินการSELECT COUNT(*) some_table
ค้นหาเดียวการคำนวณทั้งหมดจะดำเนินการที่ด้าน RDBMS Python เพียงแค่ต้องการดึงหมายเลขผลลัพธ์ด้วยต้นทุนคงที่ของ O (1)
อย่า: len(queryset)
- จะดำเนินการSELECT * FROM some_table
ค้นหาโดยดึงข้อมูลทั้งตาราง O (N) และต้องการหน่วยความจำ O (N) เพิ่มเติมเพื่อจัดเก็บ นี่เป็นสิ่งที่แย่ที่สุดที่สามารถทำได้
เมื่อคุณต้องการดึงชุด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
เปลี่ยนกลับกรณีที่ 2 (เมื่อมีการดึงข้อมูลชุดสืบค้นแล้ว):
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:
QuerySet
ใช้งานตามบริบท
ฉันคิดว่าการใช้ตรงlen(qs)
นี้มีความหมายมากกว่าเพราะคุณต้องทบทวนผลลัพธ์ qs.count()
เป็นตัวเลือกที่ดีกว่าหากทุกสิ่งที่คุณต้องการจะพิมพ์จำนวนนับและไม่วนซ้ำบนผลลัพธ์
len(qs)
จะตีฐานข้อมูลด้วยselect * from table
ในขณะที่จะตีฐานข้อมูลที่มีqs.count()
select count(*) from table
นอกจากนี้ยังqs.count()
จะให้ผลตอบแทนจำนวนเต็มและคุณไม่สามารถย้ำมากกว่านั้น
สำหรับผู้ที่ชอบการวัดทดสอบ (Postresql):
หากเรามีแบบจำลองบุคคลที่เรียบง่ายและมีอินสแตนซ์ 1,000 รายการ:
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 (เร็วกว่ามากเมื่อจัดการกับ queryset ขนาดใหญ่)นอกจากนี้ยังเป็นความจริงที่ว่าหากหลังจากการดำเนินการนี้ชุดข้อมูลทั้งหมดจะถูกทำซ้ำดังนั้นโดยรวมแล้วอาจมีประสิทธิภาพในการใช้งานlen()
มากกว่า
อย่างไรก็ตาม
ในบางกรณีตัวอย่างเช่นเมื่อมีข้อ จำกัด ของหน่วยความจำอาจเป็นเรื่องสะดวก (เมื่อเป็นไปได้) ในการแยกการดำเนินการที่ดำเนินการกับระเบียน ที่สามารถทำได้โดยใช้เลข Django
จากนั้นการใช้count()
จะเป็นทางเลือกและคุณสามารถหลีกเลี่ยงไม่ได้ที่จะต้องดึงข้อมูลชุดแบบสอบถามทั้งหมดพร้อมกัน