5

使用 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 独有的功能(使用此处给出的解决方案)

但是我如何对 PostgreSQL 做同样的事情呢?可能,使用子查询方法

4

3 回答 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
)

享受!

于 2014-09-19T23:27:33.997 回答
2

添加

WHERE analyzed <> :analyzed

以防止同一行被多次更新。或者

WHERE analyzed IS DISTINCT FROM :analyzed

如果NULL允许值。将相同的条件也添加到外部,在任何情况下UPDATE这几乎总是一个好主意,以避免空更新

一旦第一个事务完成,被ROW SHARE锁阻塞的并发事务就不会被唤醒。FOR UPDATE由于更改后的行不再通过WHERE条件,因此子查询不返回任何行并且没有任何反应。

虽然后来的事务锁定了一个新行以更新......

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

或者考虑PGQ来实现一个队列。

于 2014-09-21T01:59:49.070 回答
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()
于 2020-10-27T15:10:46.077 回答