SQLAlchemy中的条件添加语句

时间:2016-07-06 06:20:38

标签: python sql-server sql-server-2008 sqlalchemy

假设我想将几条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()       

表达式应该是什么?这样做有更清洁(更安全!)的方式吗?

2 个答案:

答案 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块通常应显示错误消息或执行数据库回滚。