SQLAlchemyで_INSERT ... ON DUPLICATE KEY UPDATE
_を行うエレガントな方法はありますか? inserter.insert().execute(list_of_dictionaries)
に似た構文を持つ何かを意味しますか?
ON DUPLICATE KEY UPDATE
ポストバージョン1.2この機能は現在、SQLAlchemy for MySQLにのみ組み込まれています。以下のsomada141の答えが最善の解決策です: https://stackoverflow.com/a/48373874/319066
ON DUPLICATE KEY UPDATE
生成されたSQLに実際にON DUPLICATE KEY UPDATE
を含めたい場合、最も簡単な方法は@compiles
デコレーターを使用することです。
例のコード(件名の良いスレッドからリンク reddit )が見つかります 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)
ただし、このアプローチでは、append_stringを手動で作成する必要があることに注意してください。おそらく、append_string関数を変更して、挿入文字列を 'ON DUPLICATE KEY UPDATE'文字列を含む挿入に自動的に変更することもできますが、ここでは怠惰であるため、ここでは行いません。
ON DUPLICATE KEY UPDATE
機能SQLAlchemyは、ORMレイヤーのON DUPLICATE KEY UPDATE
またはMERGE
またはその他の同様の機能へのインターフェイスを提供しません。それでも、問題のキーが主キーである場合にのみ機能を複製できる session.merge()
関数があります。
session.merge(ModelObject)
はまず、SELECT
クエリを送信して(またはローカルでルックアップして)、同じ主キー値を持つ行が存在するかどうかを確認します。存在する場合は、ModelObjectがすでにデータベース内にあり、SQLAlchemyがUPDATE
クエリを使用する必要があることを示すフラグをどこかに設定します。マージはこれよりもかなり複雑ですが、主キーで機能を適切に複製することに注意してください。
しかし、非主キー(たとえば、別の一意のキー)でON DUPLICATE KEY UPDATE
機能が必要な場合はどうでしょうか。残念ながら、SQLAlchemyにはそのような機能はありません。代わりに、Djangoのget_or_create()
に似たものを作成する必要があります。 別のStackOverflowの回答がそれをカバーします 、そしてここでは便宜上、変更した作業バージョンを貼り付けます。
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
V1.2リリース以降、SQLAlchemyの「コア」には上記の解決策が組み込まれており、 ここ (以下のコピーされたスニペット)で確認できることを述べておきます。
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)
phsource's answer に基づいており、MySQLを使用して同じキーのデータを完全に上書きせずに特定のユースケースにDELETE
ステートメントを実行すると、次の@compiles
装飾挿入式を使用できます。
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
それはあなた次第です。置き換える場合は、接頭辞にOR REPLACE
を渡します
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()
ここで、コミット間隔を変更してスピードアップまたはスピードダウンすることができます
より簡単な解決策を得ました:
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)
私はプレーンSQLを次のように使用しました:
insert_stmt = "REPLACE INTO tablename (column1, column2) VALUES (:column_1_bind, :columnn_2_bind) "
session.execute(insert_stmt, data)