使用SqlAlchemy和PostgreSQL更新.. LIMIT 1

时间:2014-09-19 23:13:19

标签: python postgresql sqlalchemy sql-update sql-limit

使用SqlAlchemy,是否可以构建一个只更新第一个匹配行的查询?

就我而言,我需要更新最新的日志条目:

class Log(Base):
    __tablename__ = 'logs'
    id = Column(Integer, primary_key=True)
    #...
    analyzed = Column(Boolean)

session.query(Log)  \
    .order_by(Log.id.desc())  \
    .limit(1)  \
    .update({ 'analyzed': True })

结果如下:

  

InvalidRequestError:调用limit()时无法调用Query.update()

这是有道理的,因为UPDATE ... LIMIT 1是一个仅限MySQL的功能(解决方案为here

但是我如何对PostgreSQL做同样的事情呢?可能是,使用the subquery approach

3 个答案:

答案 0 :(得分:8)

subquery recipe是正确的方法,现在我们只需要使用SqlAlchemy构建此查询。

让我们从子查询开始:

sq = ssn.query(Log.id)  \
    .order_by(Log.id.desc())  \
    .limit(1)  \
    .with_for_update()

现在与as_scalar()一起使用 使用update() docs

中的示例
from sqlalchemy import update

q = update(Log)  \
    .values({'analyzed': True})  \
    .where(Log.id == sq.as_scalar())

打印查询以查看结果:

UPDATE logs 
SET analyzed=:analyzed 
WHERE logs.id = (
    SELECT logs.id 
    FROM logs ORDER BY logs.id DESC 
    LIMIT :param_1 
    FOR UPDATE
)

享受!

答案 1 :(得分:2)

添加

WHERE analyzed <> :analyzed

防止同一行多次更新。或

WHERE analyzed IS DISTINCT FROM :analyzed

如果允许NULL值。将相同的条件添加到外部UPDATE,这对于avoid empty updates几乎总是一个好主意。

第一个事务完成后,ROW SHARE FOR UPDATE锁定阻止的并发事务将被唤醒。由于更改的行不再传递WHERE条件,因此子查询不返回任何行,没有发生。

虽然以后的交易会锁定新行以进行更新...

您可以使用advisory locks始终更新下一个未锁定的行而无需等待。我在链接的答案中添加了更多内容:

或者考虑PGQ来实现队列。

答案 2 :(得分:0)

我的数据库无法在子查询中使用限制-所以我最终使用了这样的内容:

log_id = session.query(Log.id)  \
    .order_by(Log.id.desc())  \
    .limit(1)
log_id = [log.id for log in log_id]
session.query(Log).filter(Log.id.in_(log_id)).delete()