一般来说,除非使用保证原子性的语句,否则您总是必须考虑可能由多个参与者尝试插入或更新(不要忘记删除)引起的竞争条件。即使是MERGE statement,虽然是一条语句,但如果使用不当,也可以have race conditions。
传统上,这种“upsert”是使用存储过程或其他可用的 SQL 或实现特定功能(如 MERGE 语句)执行的。
如果出现完整性错误,SQLAlchemy 解决方案必须尝试插入并执行更新,或者如果没有行受到影响,则执行 udpate 并尝试插入。它应该准备好在两个操作都失败的情况下重试(一行可能会被删除或插入):
from sqlalchemy.exc import IntegrityError
while True: # Infinite loop, use a retry counter if necessary
try:
# begin a save point, prevents the whole transaction failing
# in case of an integrity error
with session.begin_nested():
session.add(record)
# Flush instead of commit, we need the transaction intact
session.flush()
# If the flush is successful, break out of the loop as the insert
# was performed
break
except IntegrityError:
# Attempt the update. If the session has to reflect the changes
# performed by the update, change the `synchronize_session` argument.
if session.query(Class).\
filter_by(ID=record.ID).\
update({...},
syncronize_session=False):
# 1 or more rows were affected (hopefully 1)
break
# Nothing was updated, perhaps a DELETE in between
# Both operations have failed, retry
session.commit()
关于
如果在表中或要提交到表的记录中已经存在具有主键(“ID”)的记录,我想用新记录替换现有记录。
如果您可以确定不会对相关表进行并发更新,您可以使用Session.merge 来完成此类任务:
# Records have primary key set, on which merge can either load existing
# state and merge, or create a new record in session if none was found.
for record in records:
merged_record = session.merge(record)
# Note that merged_record is not record
session.commit()
SQLAlchemy 合并将首先检查具有给定主键的实例是否存在于身份映射中。如果没有,并且load 作为True 传递,它将检查数据库的主键。如果给定实例没有主键或找不到实例,则会创建一个新实例。
然后,合并会将给定实例的状态复制到定位/创建的实例上。返回新实例。