2015-07-06 9 views
113

In Django doc,Qual è la differenza tra select_related e prefetch_related in Django ORM?

select_related() "segue" le relazioni di chiave esterna, selezione dei dati correlate oggetto aggiuntivo quando esegue la sua interrogazione.

prefetch_related() esegue una ricerca separata per ogni relazione e fa il "join" in Python.

Cosa significa "fare l'unione in Python"? Qualcuno può illustrare con un esempio?

La mia comprensione è che per la relazione di chiave esterna, utilizzare select_related; e per la relazione M2M, utilizzare prefetch_related. È corretto?

+0

L'esecuzione del join in python indica che il join non verrà eseguito nel database. Con un select_related, il tuo join avviene nel database e subisci solo una query del database. Con prefetch_related, eseguirai due query e quindi i risultati saranno 'uniti' dall'ORM in modo da poter digitare object.related_set –

+0

Come nota a margine, Timmy O'Mahony può anche spiegare le loro differenze usando i risultati del database: [link] (https://timmyomahony.com/blog/misconceptions-select_related-in-django/) –

risposta

201

La tua comprensione è per lo più corretta. Si utilizza select_related quando l'oggetto che si sta per selezionare è un singolo oggetto, quindi OneToOneField o ForeignKey. Si utilizza prefetch_related quando si ottiene un "set" di cose, quindi ManyToManyField s come hai dichiarato o invertire ForeignKey s. Proprio per chiarire cosa intendo per "reverse ForeignKey s" ecco un esempio:

class ModelA(models.Model): 
    pass 

class ModelB(models.Model): 
    a = ForeignKey(ModelA) 

ModelB.objects.select_related('a').all() # Forward ForeignKey relationship 
ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship 

La differenza è che select_related fa un join SQL e ottiene i risultati indietro come parte della tabella dal server SQL di conseguenza. prefetch_related d'altra parte esegue un'altra query e quindi riduce le colonne ridondanti nell'oggetto originale (ModelA nell'esempio precedente). È possibile utilizzare prefetch_related per tutto ciò che è possibile utilizzare per select_related.

I compromessi sono che prefetch_related deve creare e inviare un elenco di ID per selezionare di nuovo al server, questo può richiedere del tempo. Non sono sicuro che ci sia un buon modo per fare questo in una transazione, ma la mia comprensione è che Django invia sempre una lista e dice SELECT ... WHERE pk IN (..., ..., ...) fondamentalmente. In questo caso, se i dati prefetched sono sparsi (diciamo che gli oggetti di stato degli Stati Uniti sono collegati agli indirizzi delle persone) questo può essere molto buono, tuttavia se è più vicino all'uno-a-uno, questo può sprecare molte comunicazioni. In caso di dubbio, provare entrambi e vedere quale funziona meglio.

Tutto ciò che viene discusso in precedenza riguarda fondamentalmente le comunicazioni con il database. Sul lato Python invece prefetch_related ha il vantaggio ulteriore di utilizzare un singolo oggetto per rappresentare ogni oggetto nel database. Con select_related verranno creati oggetti duplicati in Python per ogni oggetto "padre". Dal momento che gli oggetti in Python hanno un discreto sovraccarico di memoria, anche questo può essere una considerazione.

+1

cosa è più veloce? –

+4

'select_related' è una query mentre' prefetch_related' è due, quindi il primo è più veloce. Ma 'select_related' non ti aiuterà per' ManyToManyField''s – bhinesley

+9

@eladsilver Ci scusiamo per la risposta lenta. In realtà dipende. 'select_related' usa un JOIN nell'SQL mentre' prefetch_related' esegue la query sul primo modello, raccoglie tutti gli ID necessari per il precaricamento e quindi esegue una query con una clausola IN nel WHERE con tutti gli ID di cui ha bisogno. Se hai detto 3-5 modelli usando la stessa chiave esterna, 'select_related' sarà quasi certamente migliore. Se hai 100 o 1000 di modelli che usano la stessa chiave esterna, 'prefetch_related' potrebbe effettivamente essere migliore. Nel frattempo dovrai testare e vedere cosa succede. – CrazyCasta

7

Entrambi i metodi raggiungono lo stesso scopo, per evitare query db non necessarie. Ma usano approcci diversi per l'efficienza.

L'unico motivo per utilizzare uno di questi metodi è quando una singola query di grandi dimensioni è preferibile a molte piccole query. Django utilizza la query di grandi dimensioni per creare modelli in memoria preventivamente anziché eseguire query su richiesta sul database.

select_related esegue un join con ogni ricerca, ma estende la selezione per includere le colonne di tutte le tabelle unite. Tuttavia questo approccio ha un avvertimento.

I join hanno il potenziale di moltiplicare il numero di righe in una query. Quando si esegue un join su una chiave esterna o un campo one-to-one, il numero di righe non aumenta. Tuttavia, molti-a-molti join non hanno questa garanzia.Quindi, Django limita lo select_related alle relazioni che non si tradurranno inaspettatamente in un massiccio join.

Il "join in python" per prefetch_related è un po 'più allarmante quindi dovrebbe essere. Crea una query separata per ogni tabella da unire. Filtra ciascuno di questi tabella con una clausola IN WHERE, come:

SELECT "credential"."id", 
     "credential"."uuid", 
     "credential"."identity_id" 
FROM "credential" 
WHERE "credential"."identity_id" IN 
    (84706, 48746, 871441, 84713, 76492, 84621, 51472); 

anziché effettuare una singola join con potenzialmente troppe righe, ciascuna tabella è diviso in una query separata.

Problemi correlati