首页 > 解决方案 > 选择更新查询返回基数违规

问题描述

我们在 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如果是主键,该查询如何返回多行。

标签: postgresqlsql-updatelockingdatabase-concurrencypostgres-9.6

解决方案


看起来您可以简化为:

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

我和你一样惊讶,子查询(你不需要)会以某种方式返回不止一行。似乎不可能。您确定这是错误消息的来源吗?


推荐阅读