एक Django क्वेरी पर गणना बनाम लेन


92

Django में, यह देखते हुए कि मेरे पास एक है QuerySetकि मैं इसे खत्म करने जा रहा हूं और इसके परिणामों को प्रिंट कर रहा हूं, वस्तुओं की गिनती के लिए सबसे अच्छा विकल्प क्या है? len(qs)या qs.count()?

(यह भी दिया कि वस्तुओं को एक ही पुनरावृत्ति में गिनना एक विकल्प नहीं है।)


2
दिलचस्प सवाल। मेरा सुझाव है कि यह रूपरेखा है .. मुझे बहुत दिलचस्पी होगी! मुझे यह जानने के लिए अजगर के बारे में पर्याप्त नहीं पता है कि पूरी तरह से मूल्यांकन की गई वस्तुओं पर लेन () बहुत अधिक उपरि है। यह गिनती से तेज हो सकता है!
युजी 'तोमिता' टॉमिटा

जवाबों:


131

हालांकि Django डॉक्स केcount बजाय का उपयोग करने की सलाह देते हैं len:

नोट: len()यदि आप करना चाहते हैं तो QuerySets का उपयोग न करें , सेट में रिकॉर्ड की संख्या निर्धारित करें। यह SQL स्तर का उपयोग करके डेटाबेस स्तर पर एक गिनती को संभालने के लिए बहुत अधिक कुशल है SELECT COUNT(*), और Django count()इस कारण के लिए एक विधि प्रदान करता है ।

चूंकि आप इस QuerySet को किसी भी तरह से पुनरावृत्त कर रहे हैं, इसलिए परिणाम बंद हो जाएगा (जब तक आप उपयोग नहीं कर रहे हैं iterator), और इसलिए इसे उपयोग करना बेहतर होगा len, क्योंकि यह डेटाबेस को फिर से टालने से बचता है, और संभवतः परिणामों की एक अलग संख्या प्राप्त करने के लिए भी !) ।
यदि आप उपयोग कर रहे हैं iterator, तो मैं आपको उन्हीं कारणों से गिनती (जैसे गिनती का उपयोग करने के बजाय) से पुनरावृत्ति करने के साथ एक गिनती चर भी शामिल करने का सुझाव दूंगा।


60

के बीच चयन करना len()और count()स्थिति पर निर्भर करता है और यह गहराई से समझने के लायक है कि वे कैसे उन्हें सही तरीके से उपयोग करने के लिए काम करते हैं।

मुझे कुछ परिदृश्य प्रदान करें:

  1. (सबसे महत्वपूर्ण) जब आप केवल तत्वों की संख्या जानना चाहते हैं और आप उन्हें किसी भी तरह से संसाधित करने की योजना नहीं बनाते हैं, तो इसका उपयोग करना महत्वपूर्ण है count():

    डीओ: queryset.count() - यह एकल SELECT COUNT(*) some_tableक्वेरी करेगा , सभी गणना आरडीबीएमएस की ओर से की जाती है, पायथन को ओ (1) की निर्धारित लागत के साथ परिणाम संख्या प्राप्त करने की आवश्यकता है

    DON'T: 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 डॉक्स में अच्छे संदर्भ:


5
शानदार उत्तर, QuerySetकार्यान्वयन को प्रासंगिक रूप से पोस्ट करने के लिए +1 ।
नेहम

4
काफी शाब्दिक सही जवाब। उपयोग के बारे में बताते हुए और, इससे भी महत्वपूर्ण बात यह है कि उपयोग क्यों किया जाता है।
टॉम पेगलर

28

मुझे लगता है कि यहां उपयोग करने len(qs)से अधिक समझ में आता है क्योंकि आपको परिणामों पर पुनरावृति करने की आवश्यकता है। qs.count()एक बेहतर विकल्प है अगर आप यह करना चाहते हैं कि सभी इसे प्रिंट करें और परिणामों पर पुनरावृति न करें।

len(qs)डेटाबेस के साथ मारा जाएगा, select * from tableजबकि qs.count()db के साथ मारा जाएगा select count(*) from table

qs.count()रिटर्न पूर्णांक भी देगा और आप इस पर पुनरावृत्ति नहीं कर सकते


3

परीक्षण माप पसंद करने वाले लोगों के लिए (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()


0

दूसरों को पहले से ही जवाब दिया है जो संक्षेप में:

  • len() सभी रिकॉर्ड प्राप्त करेंगे और उन पर पुनरावृत्ति करेंगे।
  • count() SQL COUNT ऑपरेशन करेगा (बड़ी क्वेरी से निपटने पर बहुत तेज़)।

यह भी सच है कि यदि इस ऑपरेशन के बाद, पूरी क्वेरी को पुनरावृत्त किया जाएगा, तो पूरे के रूप में यह उपयोग करने के लिए थोड़ा अधिक कुशल हो सकता है len()

तथापि

कुछ मामलों में, उदाहरण के लिए, जब स्मृति सीमाएँ होती हैं, तो यह रिकॉर्ड पर किए गए ऑपरेशन को विभाजित करने के लिए सुविधाजनक (पॉसिबल) हो सकता है। कि django पेजिंग का उपयोग करके प्राप्त किया जा सकता है ।

फिर, का उपयोग count()करना पसंद होगा और आप एक बार में पूरी क्वेरी प्राप्त करने से बच सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.