使用SQLAlchemy merge()方法实现MySQL INSERT UPDATE

我们知道MySQL支持insert update,其逻辑为:如果在INSERT后导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则执行UPDATE

>>> INSERT INTO test(data_id, device_id) VALUES (1, 2) ON DUPLICATE KEY UPDATE device_id = '2'; 

注意:与REPLACE INTO逻辑不一样,REPLACE INTO是在重复后,先将该条数据删除再进行插入,所以两者效果不同
参考:https://my.oschina.net/kingdelee/blog/388589

使用SQLAlchemy 实现与INSERT UPDATE相同的效果 (略微存在差异),可以使用session.merge()

from sqlalchemy import MetaData
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session

metadata = MetaData()
metadata.reflect(engine, only=['test'])
Base = automap_base(metadata=metadata)
Base.prepare()
test = Base.classes.test
with Session(engine) as session, session.begin():
    try:
        # data_id和device_id中必须有一个为主键,没有主键而只有唯一索引会报错,这点与INSERT UPDATE不同
        session.merge(test(data_id = '1', device_id = '2'))
    except:
        session.rollback()
        raise
    else:
        session.commit()

参考:https://blog.youkuaiyun.com/u010339879/article/details/84529627


2021-12-27更新

仅支持MySQL

from sqlalchemy.dialects.mysql import insert
from sqlalchemy import Table

...

table = Table('iot_xj_liquid_fertilizer', metadata, autoload_with=engine)
insert_stmt = insert(table).values([{'data_id':'1'}, {'data_id':'2'}])
on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(data_id=insert_stmt.inserted.data_id)
result = engine.execute(on_duplicate_key_stmt)

如果想实现更新全部

from sqlalchemy.dialects.mysql import insert
from sqlalchemy import Table, text

...
data = [{'data_id':'1', 'device_id': '01'}, {'data_id':'2', 'device_id': '02'}]

table = Table('iot_xj_liquid_fertilizer', metadata, autoload_with=engine)
insert_stmt = insert(table).values(data)
kw={}
for key in data[0].keys():
    kw[key] = text(f'VALUES({key})')
on_duplicate_key_stmt = insert_stmt.on_duplicate_key_update(**kw)
result = engine.execute(on_duplicate_key_stmt)

参考:
https://stackoverflow.com/a/48373874/7151777
https://docs.sqlalchemy.org/en/14/dialects/mysql.html#insert-on-duplicate-key-update-upsert
https://www.cnblogs.com/better-farther-world2099/articles/11737376.html

根据你的描述,你在尝试使用 `MERGE INTO` 语句时遇到了 `-2007: 语法分析出错` 的问题。这是因为 `(tgt.*) = ROW(src.*)` 这种写法并不是所有数据库系统所支持的标准 SQL 语法。 大多数主流的关系型数据库(例如 MySQL、PostgreSQLSQL Server 或 Oracle),都需要明确地列出每个需要更新或插入的列名和对应的值。下面是一些调整后的解决方案: --- ### 解决方案:显式写出所有列 对于两个表结构完全一致的情况,你可以手动列出所有的列来进行 `UPDATE` 和 `INSERT` 操作。假设两表都有以下字段:`id`, `username`, `email`, `age`: ```sql MERGE INTO sys_user_back2 AS tgt USING sys_user_back1 AS src ON tgt.id = src.id WHEN MATCHED THEN UPDATE SET tgt.username = src.username, tgt.email = src.email, tgt.age = src.age WHEN NOT MATCHED THEN INSERT (id, username, email, age) VALUES (src.id, src.username, src.email, src.age); ``` 上述脚本适用于绝大多数现代 RDBMS(包括但不限于 PostgreSQLMySQL 8+、Oracle 和 SQL Server)。它逐条指定了哪些列参与了更新及新增操作。 --- ### 若需自动适应未知列数怎么办? 某些高级工具链允许动态生成 SQL,但如果纯靠手工编写的话,则建议采用编程语言结合元数据查询来自动化构建该段落逻辑。比如,在 Python + SQLAlchemy 上下文中,可通过反射机制获取实际定义并拼接最终命令文本字符串完成任务;而在 PL/pgSQL 内部存储函数里同样可行但更复杂一点。 另外值得注意的是部分特定环境或许具备专属插件扩展以提供类似功能便利性如 **dbt** 工具包等。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值