153 votes

Efficacement la mise à jour de la base de données à l'aide de SQLAlchemy ORM

Je commence une nouvelle demande et à la recherche, à l'aide d'un ORM-en particulier, SQLAlchemy.

Dire que j'ai une colonne " foo " dans ma base de données et je veux incrémenter. En ligne droite sqlite, c'est facile:

db = sqlite3.connect('mydata.sqlitedb')
cur = db.cursor()
cur.execute('update table stuff set foo = foo + 1')

J'ai compris la SQLAlchemy SQL-générateur équivalent:

engine = sqlalchemy.create_engine('sqlite:///mydata.sqlitedb')
md = sqlalchemy.MetaData(engine)
table = sqlalchemy.Table('stuff', md, autoload=True)
upd = table.update(values={table.c.foo:table.c.foo+1})
engine.execute(upd)

C'est un peu plus lent, mais il n'y a pas beaucoup en elle.

Voici ma meilleure supposition pour un SQLAlchemy ORM approche:

# snip definition of Stuff class made using declarative_base
# snip creation of session object
for c in session.query(Stuff):
    c.foo = c.foo + 1
session.flush()
session.commit()

De ce fait la bonne chose, mais il faut tout de moins de cinquante fois plus longue que les deux autres approches. Je suppose que c'est parce qu'il a pour mettre toutes les données dans la mémoire avant de pouvoir travailler avec elle.

Est-il un moyen de générer de l'efficacité de SQL à l'aide de SQLAlchemy l'ORM? Ou en utilisant tout autre python ORM? Ou devrais-je revenir à l'écriture du SQL à la main?

220voto

Ants Aasma Points 22921

SQLAlchemy l'ORM est destiné à être utilisé conjointement avec le SQL de la couche, ne pas le cacher. Mais vous n'avez pas à garder une ou deux choses à l'esprit lors de l'utilisation de l'ORM et la plaine SQL dans la même transaction. En gros, d'un côté, de l'ORM, les modifications de données ne frapper la base de données lorsque vous tirez la chasse des changements à partir de votre session. De l'autre côté, SQL instructions de manipulation de données n'affecte pas les objets qui sont dans votre session.

Donc, si vous dites

for c in session.query(Stuff).all():
    c.foo = c.foo+1
session.commit()

il fera ce qu'il dit, d'aller chercher tous les objets de la base de données, modifier tous les objets et puis quand il est temps de vider les modifications apportées à la base de données, mettre à jour les lignes une par une.

Au lieu de cela, vous devriez faire ceci:

session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1}))
session.commit()

Ceci exécutera qu'une seule requête que vous attendez, et parce que au moins le défaut de configuration de session expire toutes les données de la session de validation, vous n'avez pas de données obsolètes questions.

Dans la quasi-sortie de 0,5 série vous pouvez également utiliser cette méthode pour mettre à jour:

session.query(Stuff).update({Stuff.foo: Stuff.foo + 1})
session.commit()

Qui va exécuter la même instruction SQL comme l'extrait précédent, mais aussi de sélectionner les lignes modifiées et expirent toutes les données obsolètes dans la session. Si vous savez que vous n'utilisez pas les données de la session après la mise à jour, vous pouvez également ajouter synchronize_session=False à l'instruction de mise à jour et de se débarrasser de que choisir.

128voto

Vin Points 157
session.query(Clients).filter(Clients.id == client_id_list).update({'status': status})
session.commit()

Essayez ceci =)

2voto

Matthew Schinckel Points 15596

Si c'est à cause de la surcharge en termes de création d'objets, alors il ne peut probablement pas être accéléré avec SA.

Si c'est parce que c'est le chargement des objets, alors vous pourriez être en mesure de faire quelque chose avec un chargement différé. Y at-il beaucoup d'objets en cours de création en raison de références? (C'est à dire, l'obtention d'un objet social reçoit également toutes les Personnes objets).

1voto

Matthew Schinckel Points 15596

Withough test, je vais essayer:

for c in session.query(Stuff).all():
     c.foo = c.foo+1
session.commit()

(IIRC, commit() fonctionne sans flush()).

J'ai trouvé qu'à certains moments de faire une requête importante et puis en itérant en python peuvent être jusqu'à 2 ordres de grandeur plus rapide que beaucoup de requêtes. Je suppose que itération sur la requête de l'objet est moins efficace que d'itérer sur une liste établie par le tout (les) méthode de l'objet de requête.

[Veuillez noter que les commentaires ci-dessous - cela n'a pas accélérer les choses].

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X