UPDATE 查询中的乐观锁

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

我想执行一个更新查询,并让结果以某种方式向我指示三种可能的结果;成功,版本冲突或找不到实体

更新查询示例:

update vessel_clearance vc
    set status = "blah",
        last_updated_at = {new_ts},
        expiry = {some_time}
    where vc.id = {vesselclearanceid}
        and vc.last_updated_at = {current_version_ts}

这可能吗?我已经尝试过

WITH
子句,但到目前为止尚未成功。

sql postgresql locking common-table-expression
3个回答
2
投票

假设

vessel_clearance.id
是PK,那么下面的
UPDATE
SELECT
只能找到一行。

这里有一个单一声明来实现您的目标:

WITH
  input(id) AS MATERIALIZED (SELECT sqlc.arg(vesselclearanceid))
                               -- where does vesselclearanceid come from?
, upd AS (
   UPDATE vessel_clearance vc
   SET    status = "blah"
        , last_updated_at = {new_ts}
        , expiry = {some_time}
   FROM   input i
   WHERE  vc.id = i.id
   AND    vc.last_updated_at = {current_version_ts}
   RETURNING 1  -- see below
   )
SELECT COALESCE(
     (SELECT 'UPDATE_APPLIED'   FROM upd)
   , (SELECT 'VERSION_CONFLICT' FROM vessel_clearance JOIN input USING (id))
           , 'ENTITY_NOT_FOUND'
   ) AS result;

sqlc.arg()
是一个功能,我对是否
STABLE
一无所知,或者可能会有副作用。所以我将其移至
MATERIALIZED
CTE
以确保它只被调用一次。您可能需要也可能不需要。

RETURNING 1
因为只需要返回一行即可。我的查询不关心返回值。

仅当

UPDATE
实际发生时,该行才会被锁定。
SELECT
不会锁定 Postgres 中的行。 “乐观锁定”就到此为止了。
在这种情况下,外部
SELECT
返回“UPDATE_APPLIED”。

如果且仅当它没有发生时,另一个

SELECT
会检查给定 ID 是否存在。如果找到,则返回唯一剩余的答案“VERSION_CONFLICT”。

否则我们会得到“ENTITY_NOT_FOUND”。

值得注意的是,这是一个单一的声明。 CTE 和外部

SELECT
,都看到表的相同快照。所以不存在竞争条件。


0
投票

好问题。

我认为您无法在单个查询中完成此操作。

UPDATE 语句将返回受影响的行数,该行数直接对应于与搜索谓词值 true(不是未知,也不是 false)匹配的行。如果搜索谓词包含版本验证,则它不会匹配不同的版本值,并且将返回零,即使存在部分匹配谓词的行(仅适用于本例中的

id
)。

话虽如此,您可以使用悲观锁定来做到这一点。运行一个查询来检查谓词 #1(不带版本验证),然后运行第二个查询来检查谓词 #1(带版本验证)。当然,这违背了使用乐观锁的目标。


0
投票

请尝试以下操作。它将返回行数。

也就是说,如果您使用其他语言的库,更新/删除/插入方法通常会返回受影响的行数。

with update_query as (
  update vessel_clearance vc
     set status = "blah",
         last_updated_at = {new_ts},
         expiry = {some_time}
   where vc.id = sqlc.arg(vesselclearanceid)
     and vc.last_updated_at = {current_version_ts}
   returning id
)
select count(*) from update_query;
© www.soinside.com 2019 - 2024. All rights reserved.