Python SQLAlchemy关于重复密钥更新
有没有一种优雅的方式来做Python SQLAlchemy关于重复密钥更新,python,mysql,sqlalchemy,Python,Mysql,Sqlalchemy,有没有一种优雅的方式来做插入。。。SQLAlchemy中的重复密钥更新?我指的是语法类似于inserter.insert().execute(字典列表)?的东西,用于MySQL的复制密钥更新 此功能现在仅内置于MySQL的SQLAlchemy中。somada141下面的答案是最好的解决方案: SQL语句中重复密钥更新时的 如果希望生成的SQL在重复密钥更新时实际包含,最简单的方法是使用@compiles装饰器 示例代码(链接自主题上的一个好线程)可以找到: 从sqlalchemy.ext.co
插入。。。SQLAlchemy中的重复密钥更新
?我指的是语法类似于inserter.insert().execute(字典列表)
?的东西,用于MySQL的复制密钥更新
此功能现在仅内置于MySQL的SQLAlchemy中。somada141下面的答案是最好的解决方案:
SQL语句中重复密钥更新时的
如果希望生成的SQL在重复密钥更新时实际包含,最简单的方法是使用@compiles
装饰器
示例代码(链接自主题上的一个好线程)可以找到:
从sqlalchemy.ext.compiler导入编译
从sqlalchemy.sql.expression导入插入
@编译(插入)
def append_字符串(插入,编译器,**kw):
s=编译器。访问\u插入(插入,**kw)
如果insert.kwargs中的“append_string”:
返回s+“”+insert.kwargs['append_string']
返回s
my_connection.execute(my_table.insert(append_string='ON replicate KEY UPDATE foo=foo'),my_值)
但是请注意,在这种方法中,您必须手动创建append_字符串。您可能会更改append_string函数,使其自动将insert字符串更改为带有“ON DUPLICATE KEY UPDATE”字符串的insert,但由于懒惰,我不打算在这里这样做
关于ORM内的重复密钥更新
功能
SQLAlchemy在其ORM层中不提供与重复密钥更新或合并或任何其他类似功能的接口。然而,只有当所讨论的键是主键时,它才具有复制功能的功能
session.merge(ModelObject)
首先通过发送SELECT
查询(或通过本地查找)检查是否存在主键值相同的行。如果是,它会在某个地方设置一个标志,指示ModelObject已经在数据库中,并且SQLAlchemy应该使用UPDATE
查询。请注意,merge比这要复杂得多,但它通过主键很好地复制了功能
但是,如果您想使用非主键(例如,另一个唯一键)更新重复键
功能,该怎么办?不幸的是,SQLAlchemy没有任何这样的功能。相反,您必须创建类似于Django的get\u或\u create()
,为了方便起见,我会在这里粘贴一个经过修改的工作版本
def get_或_create(会话、模型、默认值=None,**kwargs):
instance=session.query(model).filter_by(**kwargs).first()
例如:
返回实例
其他:
params=dict((k,v)表示kwargs.iteritems()中的k,v,如果不存在(v,ClauseElement))
如果为默认值:
参数更新(默认值)
实例=模型(**参数)
返回实例
得到了一个更简单的解决方案:
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,而且它可以处理非orm内容。我只是将普通sql用作:
insert_stmt = "REPLACE INTO tablename (column1, column2) VALUES (:column_1_bind, :columnn_2_bind) "
session.execute(insert_stmt, data)
这取决于你。如果要替换,请在前缀中传递或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()
在这里,提交间隔可以更改为加速或减速,对于使用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
我应该提到的是,自v1.2发行版以来,SQLAlchemy“核心”就有了一个解决上述问题的内置解决方案,可以在下面看到(下面复制的代码片段):
我的方式
导入键入
从日期时间导入日期时间
从sqlalchemy.dialogs导入mysql
类别MyRepository:
def型号(自):
返回MySqlAlchemyModel
def upsert(self,数据:typing.List[typing.Dict]):
如果没有数据:
返回
model=self.model()
如果hasattr(模型“已创建”:
对于数据中的项目:
项['created_at']=datetime.now()
stmt=mysql.insert(getattr(model,'.'表'.'')).values(data)
for_update=[]
对于数据[0]中的k,v。项()
对于_update.append(k)
dup={k:getattr(stmt.inserted,k)表示k in for_update}
stmt=stmt.on\u duplicate\u key\u update(**dup)
self.db.session.execute(stmt)
self.db.session.commit()
用法:
myrepo.upsert([
{
“字段11”:“值11”,
“field21”:“value21”,
“field31”:“value31”,
},
{
“字段12”:“值12”,
“field22”:“value22”,
“field32”:“value32”,
},
])
其他答案已经涵盖了这一点,但我想我会参考gist中找到的mysql的另一个好例子。这还包括使用LAST\u INSERT\u ID
,这可能会很有用,具体取决于innodb自动增量设置以及表是否具有唯一键。在这里提升代码以便于参考,但如果您觉得有用,请给作者一个星号
from app import db
from sqlalchemy import func
from sqlalchemy.dialects.mysql import insert
def upsert(model, insert_dict):
"""model can be a db.Model or a table(), insert_dict should contain a primary or unique key."""
inserted = insert(model).values(**insert_dict)
upserted = inserted.on_duplicate_key_update(
id=func.LAST_INSERT_ID(model.id), **{k: inserted.inserted[k]
for k, v in insert_dict.items()})
res = db.engine.execute(upserted)
return res.lastrowid
保重<代码>替换为
和插入。。。在重复键更新时,执行不同的操作。值得注意的是,它会删除行,因此此解决方案通常在InnoDB
(或任何其他事务引擎)表上非常无用,因为它会阻塞大多数外键
约束。它可以与MySql配合使用。话虽如此,我在那张表上没有任何外键。请注意,append\u字符串
code在postgres上是不起作用的(9.5中新增了on CONFLICT[IGNORE | UPDATE]
特性,因为ORM会自动追加一个返回
from app import db
from sqlalchemy import func
from sqlalchemy.dialects.mysql import insert
def upsert(model, insert_dict):
"""model can be a db.Model or a table(), insert_dict should contain a primary or unique key."""
inserted = insert(model).values(**insert_dict)
upserted = inserted.on_duplicate_key_update(
id=func.LAST_INSERT_ID(model.id), **{k: inserted.inserted[k]
for k, v in insert_dict.items()})
res = db.engine.execute(upserted)
return res.lastrowid