Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python SQLAlchemy关于重复密钥更新_Python_Mysql_Sqlalchemy - Fatal编程技术网

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