使用 SqlAlchemy 和 PostgreSQL 更新 .. LIMIT 1

问题描述 投票:0回答:3

使用 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 做同样的事情呢?可能,使用子查询方法

python postgresql sqlalchemy sql-update sql-limit
3个回答
8
投票

子查询配方是正确的方法,现在我们只需要使用SqlAlchemy构建这个查询。

让我们从子查询开始:

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

现在将其与 as_scalar() 一起使用 使用 update() 文档中的示例:

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
)

享受吧!


2
投票

为了防止同一行被多次更新,添加:

WHERE analyzed <> :analyzed

或者如果允许

NULL
值:

WHERE analyzed IS DISTINCT FROM :analyzed

向外部

UPDATE
添加相同的条件,在任何情况下这几乎总是一个好主意,以避免空更新

一旦第一个事务完成,被

ROW SHARE

 中的 
FOR UPDATE
 锁阻止的并发事务就会被唤醒。由于更改的行不再通过 
WHERE
 条件,因此子查询不会返回任何行,并且 
什么也没有发生。 虽然稍后的事务会锁定新行来更新...

您可以使用

咨询锁

始终更新下一个未锁定的行,而无需等待。更多详情请点击这里:

    Postgres 更新...限制 1
  • 还有相关的
PGQ

来实现队列。 (我自己没用过)。


1
投票

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()

© www.soinside.com 2019 - 2024. All rights reserved.