Mysql
 sql >> Base de données >  >> RDS >> Mysql

Mieux comprendre les problèmes `yield_per()` de SQLalchemy

Les deux stratégies de chargement problématiques génèrent des exceptions si vous essayez de les utiliser avec yield_per , vous n'avez donc pas vraiment à vous inquiéter.

Je crois le seul problème avec subqueryload est que le chargement par lots de la deuxième requête n'est pas (encore) implémenté. Rien n'irait mal sémantiquement, mais si vous utilisez yield_per , vous avez probablement une très bonne raison de ne pas vouloir charger tous les résultats en même temps. SQLAlchemy refuse donc poliment d'aller à l'encontre de vos souhaits.

joinedload est un peu plus subtil. Ce n'est interdit que dans le cas d'une collection, où une ligne principale peut avoir plusieurs lignes associées. Supposons que votre requête produise des résultats bruts comme celui-ci, où A et B sont des clés primaires de différentes tables :

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

Maintenant, vous les récupérez avec yield_per(3) . Le problème est que SQLAlchemy ne peut limiter la quantité qu'il récupère que par lignes , mais il doit renvoyer des objets . Ici, SQLAlchemy ne voit que les trois premières lignes, il crée donc un A objet avec la clé 1 et trois B enfants :1, 2 et 3.

Lorsqu'il charge le lot suivant, il veut créer un nouveau A objet avec la clé 1... ah, mais il en a déjà un, donc pas besoin de le recréer. Le B supplémentaire , 4, est perdu. (Donc, non, même lire des collections jointes avec yield_per n'est pas sûr - des morceaux de vos données pourraient disparaître.)

Vous pourriez dire "eh bien, continuez à lire les lignes jusqu'à ce que vous ayez un objet complet" - mais que se passe-t-il si ce A a cent enfants? Ou un million ? SQLAlchemy ne peut raisonnablement pas garantir qu'il peut faire ce que vous avez demandé et produire des résultats corrects, il refuse donc d'essayer.

N'oubliez pas que la DBAPI est conçue pour que tout La base de données peut être utilisée avec la même API, même si cette base de données ne prend pas en charge toutes les fonctionnalités DBAPI. Considérez que le DBAPI est conçu autour des curseurs, mais que MySQL n'en a pas réellement en a curseurs ! Les adaptateurs DBAPI pour MySQL doivent les simuler à la place.

Ainsi, tandis que cursor.fetchmany(100) va fonctionner , vous pouvez voir à partir de le MySQLdb code source qu'il ne récupère pas paresseusement du serveur ; il récupère tout dans une grande liste, puis renvoie une tranche lorsque vous appelez fetchmany .

Qu'est-ce que psycopg2 prend en charge le vrai streaming, où les résultats sont mémorisés de manière permanente sur le serveur, et votre processus Python n'en voit que quelques-uns à la fois.

Vous pouvez toujours utiliser yield_per avec MySQLdb , ou toute autre DBAPI ; c'est tout l'intérêt de la conception du DBAPI. Vous devrez payer le coût de la mémoire pour toutes les lignes brutes cachées dans la DBAPI (qui sont des tuples, assez bon marché), mais vous ne le ferez pas aussi avoir à payer pour tous les objets ORM en même temps.