选择更新查询返回基数违规

时间:2019-06-03 23:05:04

标签: postgresql sql-update locking database-concurrency postgres-9.6

我们正在Google托管的云数据库的Postgres 9.6.10中运行此查询:

WITH update AS
  (UPDATE cart SET loyalty = loyalty || jsonb_insert('{}', '{coupon}', loyalty#>'{scan_coupon}' || $1) WHERE id = 
  (SELECT id FROM cart WHERE id = $2 AND status = $3 and item_version = $4 FOR UPDATE) returning *)
SELECT * FROM updated

cart是一个以id作为主键的表。 loyalty是一个jsonb列,而item_version是一个在某些操作上递增的函数,但预计在更新item_version之前会进行几次更新。 status是枚举类型。

在高度并发的更新下,我们很少会出现以下错误:

Cardinality_violation, file: "nodeSubplan.c", line: "1127", message: "more than one row returned by a subquery used as an expression", pg_code: "21000", routine: "ExecSetParamPlan", severity: "ERROR", unknown: "ERROR"

我已经确认$2实际上是一个整数,并指向现有的行,并且由于id是主键,因此我看不到它怎么可能会返回多行。

SELECT FOR UPDATE才是有问题的查询吗?如果id是主键,该查询如何返回多个行。

1 个答案:

答案 0 :(得分:2)

看起来您可以简化为:

UPDATE cart
SET    loyalty = loyalty || jsonb_build_object('coupon', loyalty->'scan_coupon') || $1
WHERE  id  =  $2
AND    status = $3
AND    item_version = $4
RETURNING *;

UPDATE的锁定方式与嵌套SELECT ... FOR UPDATE相同。

jsonb_build_object()比较简单,其作用与您的jsonb_insert()相同。或更简单一些:

SET    loyalty = jsonb_insert(loyalty, '{coupon}', loyalty->'scan_coupon') || $1

让您感到惊讶的是,子查询(不需要)将以某种方式返回多个行。似乎不可能。您确定这是错误消息的来源吗?