Mysql
 sql >> Database >  >> RDS >> Mysql

Comprendere meglio i problemi `yield_per()` di SQLalchemy

Entrambe le strategie di caricamento problematiche sollevano eccezioni se provi a usarle con yield_per , quindi non devi preoccuparti troppo.

credo l'unico problema con subqueryload è che il caricamento in batch della seconda query non è (ancora) implementato. Nulla andrebbe storto semanticamente, ma se stai usando yield_per , probabilmente hai una buona ragione per non voler caricare tutti i risultati in una volta. Quindi SQLAlchemy si rifiuta educatamente di andare contro i tuoi desideri.

joinedload è un po' più sottile. È vietato solo nel caso di una raccolta, in cui una riga primaria potrebbe avere più righe associate. Supponiamo che la tua query produca risultati grezzi come questo, dove A e B sono chiavi primarie di tabelle diverse:

 A | B 
---+---
 1 | 1 
 1 | 2 
 1 | 3 
 1 | 4 
 2 | 5 
 2 | 6 

Ora li prendi con yield_per(3) . Il problema è che SQLAlchemy può limitare solo quanto recupera per righe , ma deve restituire oggetti . Qui, SQLAlchemy vede solo le prime tre righe, quindi crea un A oggetto con chiave 1 e tre B bambini:1, 2 e 3.

Quando carica il batch successivo, vuole creare un nuovo A oggetto con chiave 1... ah, ma ne ha già uno, quindi non è necessario crearlo di nuovo. L'extra B , 4, è perso. (Quindi no, anche leggere le raccolte unite con yield_per non è sicuro:parti dei tuoi dati potrebbero andare perse.)

Potresti dire "beh, continua a leggere le righe finché non hai un oggetto completo" - ma cosa succede se quel A ha cento figli? O un milione? SQLAlchemy non può ragionevolmente garantire che possa fare ciò che hai chiesto e produrre risultati corretti, quindi si rifiuta di provare.

Ricorda che la DBAPI è progettata in modo che qualsiasi il database può essere utilizzato con la stessa API, anche se il database non supporta tutte le funzionalità DBAPI. Considera che il DBAPI è progettato attorno ai cursori, ma MySQL in realtà non ha cursori! Gli adattatori DBAPI per MySQL devono invece simularli.

Quindi, mentre cursor.fetchmany(100) funziona , puoi vedere da il MySQLdb codice sorgente che non prenda pigramente dal server; recupera tutto in un unico grande elenco, quindi restituisce una sezione quando chiami fetchmany .

Cosa psycopg2 supporta è il vero streaming, in cui i risultati vengono ricordati in modo persistente sul server e il tuo processo Python ne vede solo alcuni alla volta.

Puoi ancora utilizzare yield_per con MySQLdb o qualsiasi altro DBAPI; questo è il punto centrale del design del DBAPI. Dovrai pagare il costo della memoria per tutte le righe grezze nascoste nel DBAPI (che sono tuple, abbastanza economiche), ma non lo farai anche pagare tutti gli oggetti ORM contemporaneamente.