我想执行一个更新查询,并让结果以某种方式向我指示三种可能的结果;成功,版本冲突或找不到实体
更新查询示例:
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
子句,但到目前为止尚未成功。
假设
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
,都看到表的相同快照。所以不存在竞争条件。
好问题。
我认为您无法在单个查询中完成此操作。
UPDATE 语句将返回受影响的行数,该行数直接对应于与搜索谓词值 true(不是未知,也不是 false)匹配的行。如果搜索谓词包含版本验证,则它不会匹配不同的版本值,并且将返回零,即使存在部分匹配谓词的行(仅适用于本例中的
id
)。
话虽如此,您可以使用悲观锁定来做到这一点。运行一个查询来检查谓词 #1(不带版本验证),然后运行第二个查询来检查谓词 #1(带版本验证)。当然,这违背了使用乐观锁的目标。
请尝试以下操作。它将返回行数。
也就是说,如果您使用其他语言的库,更新/删除/插入方法通常会返回受影响的行数。
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;