使用 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 做同样的事情呢?可能,使用子查询方法?
子查询配方是正确的方法,现在我们只需要使用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
)
享受吧!
为了防止同一行被多次更新,添加:
WHERE analyzed <> :analyzed
或者如果允许
NULL
值:
WHERE analyzed IS DISTINCT FROM :analyzed
向外部
UPDATE
添加相同的条件,在任何情况下这几乎总是一个好主意,以避免空更新。
一旦第一个事务完成,被 ROW SHARE
中的
FOR UPDATE
锁阻止的并发事务就会被唤醒。由于更改的行不再通过
WHERE
条件,因此子查询不会返回任何行,并且什么也没有发生。 虽然稍后的事务会锁定新行来更新...
您可以使用
咨询锁PGQlog_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()