2013-01-14 24 views
49

In Django, dato che ho un QuerySet su cui ho intenzione di scorrere e stampare i risultati di, qual è l'opzione migliore per il conteggio degli oggetti? len (qs) o qs.count()?Count vs len su un Django QuerySet

(dato anche che a contare gli oggetti nella stessa iterazione non è un'opzione)

+2

Domanda interessante. Suggerisco di profilare questo .. Sarei molto interessato! Non so abbastanza su Python per sapere se len() su oggetti completamente valutati ha molto overhead. Potrebbe essere più veloce del conteggio! –

risposta

72

Anche se il Django docs consiglia di utilizzare count piuttosto che len:

Nota: non utilizzare len() sul set di query se tutto quello che vuoi fare è determinare il numero di record nel set. È molto più efficiente gestire un conteggio a livello di database, utilizzando SQL SELECT COUNT(*) e Django fornisce un metodo count() proprio per questo motivo.

Dal momento che si effettua l'iterazione questo QuerySet in ogni caso, the result will be cached (a meno che non si sta utilizzando iterator), e così sarà preferibile utilizzare len, dal momento che questo evita di colpire nuovamente il database, e anche il forse di recuperare un diverso numero di risultati!).
Se si utilizza iterator, suggerirei di includere una variabile di conteggio mentre si scorre (anziché utilizzare il conteggio) per gli stessi motivi.

23

Penso che usare len(qs) abbia più senso qui perché è necessario scorrere i risultati. qs.count() è un'opzione migliore se tutto ciò che vuoi fare è stampare il conteggio e non iterare sui risultati.

len(qs) colpirà il database con select * from table mentre qs.count() colpirà il db con select count(*) from table.

anche qs.count() darà intero di ritorno e non è possibile scorrere su di esso

6

Scelta tra len() e count() dipende dalla situazione e vale la pena di capire a fondo come funzionano usarli correttamente.

Lasciatemi fare con alcuni scenari:

  1. (più importante) Quando si desidera solo conoscere il numero di elementi e non si prevede di trattare in alcun modo è fondamentale utilizzare count():

    DO:queryset.count() - questo eseguirà singolo SELECT COUNT(*) some_table ricerca, tutti i calcoli viene eseguita sul lato RDBMS, Python ha solo bisogno di recuperare il numero risultato con costo fisso di O (1)

    01.235.

    DO:len(queryset) - questo eseguirà la query SELECT * FROM some_table, recuperando l'intera tabella O (N) e richiedendo memoria O (N) aggiuntiva per la memorizzazione.Questo è il peggiore che si può fare

  2. Quando si ha intenzione di prendere il set di query in ogni caso è un po 'meglio usare len() che non causerà una query di database di extra come count() farebbe:

    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 
    

    Conte :

    queryset.count() # this will perform an extra db query - len() did not 
    
    for obj in queryset: # fetching data 
        pass 
    
  3. Ripristinata secondo caso (quando set di query è già stata scaricata):

    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) 
    

Tutto sarà chiaro una volta si prende un colpo d'occhio "sotto il cofano":

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) 

buoni riferimenti in docs Django:

Problemi correlati