我们正在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
是主键,该查询如何返回多个行。
答案 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
让您感到惊讶的是,子查询(不需要)将以某种方式返回多个行。似乎不可能。您确定这是错误消息的来源吗?