假设我想将几条SQL记录上传到可能尚未填充的表中。如果存在主键(" ID")的记录,无论是在表中还是要提交到表中的记录中,我都希望用新记录替换现有记录。 我正在使用mssql,SQL server 2008。
我的第一个猜测是
try:
session.add(record)
session.commit
except:
session.query().\
filter(Class.ID == record.ID).\
update(some expression)
session.commit()
表达式应该是什么?这样做有更清洁(更安全!)的方式吗?
答案 0 :(得分:2)
一般情况下,除非使用保证原子性的陈述,否则您必须始终考虑可能由于多个演员试图插入或更新(不要忘记删除)而引起的竞争条件。即使单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
传递,它将检查数据库中的主键。如果给定实例没有主键或找不到实例,则会创建一个新实例。
然后,合并将复制给定实例的状态到已定位/创建的实例。返回新实例。
答案 1 :(得分:0)
没有。这样做有一个更好的模式。首先查询该记录是否已存在,然后相应地继续。
使用您的语法,它将类似于以下内容:
result = session.query().filter(Class.ID == record.ID).first()
# If record does not exist in Db, then add record
if result is None:
try:
session.add(record)
session.commit()
except:
db.rollback()
log.error('Rolling back transaction in query-none block')
# If record does exist, then update value of record in Db
else:
try:
session.query().\
filter(Class.ID == record.ID).\
update(some expression)
session.commit()
except:
db.rollback()
log.error('Rolling back transaction')
将数据库操作包装在try / except块中通常是一个好主意,因此您可以使用所写内容的try-part来处于正确的轨道上。根据您正在执行的操作,except块通常应显示错误消息或执行数据库回滚。