UPDATE .. LIMIT 1 with SqlAlchemy and PostgreSQL

↘锁芯ラ 提交于 2019-12-09 03:16:55

问题


With SqlAlchemy, is it possible to build a query which will update only the first matching row?

In my case, I need to update the most recent log entry:

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

Which results into:

InvalidRequestError: Can't call Query.update() when limit() has been called

It makes sense, since UPDATE ... LIMIT 1 is a MySQL-only feature (with the solution given here)

But how would I do the same with PostgreSQL? Possibly, using the subquery approach?


回答1:


The subquery recipe is the right way to do it, now we only need to build this query with SqlAlchemy.

Let's start with the subquery:

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

And now use it with as_scalar() with the example from the update() docs:

from sqlalchemy import update

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

Print the query to have a look at the result:

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

Enjoy!




回答2:


Add

WHERE analyzed <> :analyzed

to prevent the same row from being updated multiple times. Or

WHERE analyzed IS DISTINCT FROM :analyzed

if NULL values are allowed. Add the same condition to the outer UPDATE as well, which is almost always a good idea in any case to avoid empty updates.

Concurrent transactions being blocked by the ROW SHARE lock from FOR UPDATE wake up as soon as the first transaction finishes. Since the changed row does not pass the WHERE condition any more, the subquery returns no row and nothing happens.

While later transactions lock a new row to update ...

You could use advisory locks to always update the next unlocked row without waiting. I added more at the linked answer:

  • Postgres UPDATE ... LIMIT 1

Or consider PGQ to implement a queue.



来源:https://stackoverflow.com/questions/25943616/update-limit-1-with-sqlalchemy-and-postgresql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!