sql >> Base de Datos >  >> RDS >> Mysql

Entender mejor los problemas `yield_per()` de SQLalchemy

Ambas estrategias de carga problemáticas generan excepciones si intenta usarlas con yield_per , para que no tengas que preocuparte demasiado.

Yo creo el único problema con subqueryload es que la carga por lotes de la segunda consulta no está implementada (todavía). Nada saldría mal semánticamente, pero si está usando yield_per , probablemente tenga una muy buena razón para no querer cargar todos los resultados a la vez. Entonces SQLAlchemy se niega cortésmente a ir en contra de sus deseos.

joinedload es un poco más sutil. Solo está prohibido en el caso de una colección, donde una fila principal puede tener varias filas asociadas. Digamos que su consulta produce resultados sin procesar como este, donde A y B son claves principales de diferentes tablas:

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

Ahora los obtienes con yield_per(3) . El problema es que SQLAlchemy solo puede limitar cuánto obtiene por filas , pero tiene que devolver objetos . Aquí, SQLAlchemy solo ve las primeras tres filas, por lo que crea un A objeto con clave 1 y tres B niños:1, 2 y 3.

Cuando carga el siguiente lote, quiere crear un nuevo A objeto con la clave 1... ah, pero ya tiene uno de esos, así que no es necesario crearlo de nuevo. La B adicional , 4, se pierde. (Entonces, no, incluso leer colecciones unidas con yield_per no es seguro:es posible que se pierdan fragmentos de sus datos).

Podría decir "bueno, siga leyendo las filas hasta que tenga un objeto completo", pero ¿y si ese A tiene cien hijos? ¿O un millón? SQLAlchemy no puede garantizar razonablemente que pueda hacer lo que usted pidió y producir resultados correctos, por lo que se niega a intentarlo.

Recuerde que la DBAPI está diseñada para que cualquier La base de datos se puede usar con la misma API, incluso si esa base de datos no es compatible con todas las funciones de DBAPI. Considere que DBAPI está diseñado alrededor de cursores, pero MySQL en realidad no tiene cursores! En cambio, los adaptadores DBAPI para MySQL tienen que falsificarlos.

Entonces mientras cursor.fetchmany(100) funcionará , puede ver desde el MySQLdb código fuente que no se recupera perezosamente del servidor; obtiene todo en una gran lista, luego devuelve un segmento cuando llama a fetchmany .

Qué psycopg2 admite la transmisión real, donde los resultados se recuerdan persistentemente en el servidor, y su proceso de Python solo ve algunos de ellos a la vez.

Todavía puedes usar yield_per con MySQLdb , o cualquier otro DBAPI; ese es el objetivo del diseño de DBAPI. Tendrá que pagar el costo de la memoria por todas las filas sin procesar ocultas en el DBAPI (que son tuplas, bastante baratas), pero no también tiene que pagar por todos los objetos ORM al mismo tiempo.