Existe-t-il un moyen élégant de créer un INSERT ... ON DUPLICATE KEY UPDATE
dans SQLAlchemy? Je veux dire quelque chose avec une syntaxe similaire à inserter.insert().execute(list_of_dictionaries)
?
ON DUPLICATE KEY UPDATE
après la version 1.2 pour MySQLCette fonctionnalité est maintenant intégrée à SQLAlchemy pour MySQL uniquement. La réponse ci-dessous de somada141 offre la meilleure solution: https://stackoverflow.com/a/48373874/319066
ON DUPLICATE KEY UPDATE
dans l'instruction SQLSi vous voulez que le SQL généré inclue réellement ON DUPLICATE KEY UPDATE
, le moyen le plus simple consiste à utiliser un décorateur @compiles
.
Le code (lié à un bon fil sur le sujet sur reddit ) pour un exemple peut être trouvé sur github :
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert
@compiles(Insert)
def append_string(insert, compiler, **kw):
s = compiler.visit_insert(insert, **kw)
if 'append_string' in insert.kwargs:
return s + " " + insert.kwargs['append_string']
return s
my_connection.execute(my_table.insert(append_string = 'ON DUPLICATE KEY UPDATE foo=foo'), my_values)
Mais notez que dans cette approche, vous devez créer manuellement append_string. Vous pourriez probablement changer la fonction append_string pour qu'elle change automatiquement la chaîne d'insertion en une insertion avec la chaîne 'ON DUPLICATE KEY UPDATE', mais je ne le ferai pas ici à cause de la paresse.
ON DUPLICATE KEY UPDATE
fonctionnalité dans l'ORMSQLAlchemy ne fournit pas d'interface avec ON DUPLICATE KEY UPDATE
ou MERGE
ni aucune autre fonctionnalité similaire dans sa couche ORM. Néanmoins, il possède la fonction session.merge()
qui peut répliquer la fonctionnalité uniquement si la clé en question est une clé primaire.
session.merge(ModelObject)
vérifie d'abord si une ligne avec la même valeur de clé primaire existe en envoyant une requête SELECT
(ou en la recherchant localement). Si tel est le cas, il définit un indicateur quelque part indiquant que ModelObject est déjà dans la base de données et que SQLAlchemy doit utiliser une requête UPDATE
. Notez que la fusion est un peu plus compliquée que cela, mais elle reproduit bien la fonctionnalité avec les clés primaires.
Mais que faire si vous voulez une fonctionnalité ON DUPLICATE KEY UPDATE
avec une clé non primaire (par exemple, une autre clé unique)? Malheureusement, SQLAlchemy n’a pas une telle fonction. Au lieu de cela, vous devez créer quelque chose qui ressemble à la fonction get_or_create()
de Django. Une autre réponse de StackOverflow le couvre , et je vais simplement coller une version modifiée et fonctionnelle ici pour plus de commodité.
def get_or_create(session, model, defaults=None, **kwargs):
instance = session.query(model).filter_by(**kwargs).first()
if instance:
return instance
else:
params = dict((k, v) for k, v in kwargs.iteritems() if not isinstance(v, ClauseElement))
if defaults:
params.update(defaults)
instance = model(**params)
return instance
Je devrais mentionner que depuis la version 1.2, le "noyau" de SQLAlchemy a une solution à ce qui précède, qui est intégrée et peut être vu sous ici (extrait copié ci-dessous):
from sqlalchemy.dialects.mysql import insert
insert_stmt = insert(my_table).values(
id='some_existing_id',
data='inserted value')
on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(
data=insert_stmt.inserted.data,
status='U'
)
conn.execute(on_duplicate_key_stmt)
Sur la base de (réponse de phsource }, et du cas d'utilisation spécifique de MySQL et du remplacement complet des données de la même clé sans effectuer une instruction DELETE
, vous pouvez utiliser l'expression d'insertion décorée @compiles
décorée suivante:
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert
@compiles(Insert)
def append_string(insert, compiler, **kw):
s = compiler.visit_insert(insert, **kw)
if insert.kwargs.get('on_duplicate_key_update'):
fields = s[s.find("(") + 1:s.find(")")].replace(" ", "").split(",")
generated_directive = ["{0}=VALUES({0})".format(field) for field in fields]
return s + " ON DUPLICATE KEY UPDATE " + ",".join(generated_directive)
return s
Cela dépend de vous. Si vous voulez remplacer, passez OR REPLACE
dans les préfixes
def bulk_insert(self,objects,table):
#table: Your table class and objects are list of dictionary [{col1:val1, col2:vale}]
for counter,row in enumerate(objects):
inserter = table.__table__.insert(prefixes=['OR IGNORE'], values=row)
try:
self.db.execute(inserter)
except Exception as E:
print E
if counter % 100 == 0:
self.db.commit()
self.db.commit()
Ici, l'intervalle de validation peut être modifié pour accélérer ou réduire
Vous avez une solution plus simple:
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Insert
@compiles(Insert)
def replace_string(insert, compiler, **kw):
s = compiler.visit_insert(insert, **kw)
s = s.replace("INSERT INTO", "REPLACE INTO")
return s
my_connection.execute(my_table.insert(replace_string=""), my_values)
Je viens d'utiliser plain SQL comme:
insert_stmt = "REPLACE INTO tablename (column1, column2) VALUES (:column_1_bind, :columnn_2_bind) "
session.execute(insert_stmt, data)