Ara*_*our 4 postgresql update json postgresql-10
我在 PostgreSQL 10 数据库product
中有一个jsonb
名为“元数据”的列的表。这是我第一次使用文档和 Postgres。jsonb
值看起来像这样:
{ "name": "l33t 衬衫", “价格”:“1200”, "数量": "60", “选项” : { “类型”:“收音机”, "title": "颜色", “选择”:[ {“价值”:“红色”,“价格”:“-100”,“数量”:“30”}, {“价值”:“蓝色”,“价格”:“+200”,“数量”:“10”}, {“价值”:“绿色”,“价格”:“+300”,“数量”:“20”} ] } }
两个问题:
1.如何选择“opts”数组中的特定元素?
select metadata->'options'->'opts'->(element here) from product
where metadata->'options'->'opts' @> '[{"value" : "blue"}]'
Run Code Online (Sandbox Code Playgroud)
2 、当售出一件或多件时,如何更新“数量”(减去当前的“数量”)?
对指南/注释的进一步链接表示赞赏。
- 如何选择“opts”数组中的特定元素?
按照手册中的说明使用元素编号的索引。对于较长的路径,路径符号较短:
SELECT metadata -> 'options' -> 'opts' -> 0 AS elem0
, metadata #> '{options, opts, 0}' AS elem0_path
FROM product;
Run Code Online (Sandbox Code Playgroud)
获取第n 个元素,就像问题标题所要求的一样(JSON 数组索引从 0 开始)。但是您的示例表明您实际上想要带有"value":"blue"
. 这可不是小事。您可以jsonb_array_elements()
在LATERAL
连接中取消嵌套的 JSON 数组并过滤您想要的数组。
SELECT opt AS elem_blue
-- , metadata #> '{options, opts}' -> (arr.ord::int - 1) AS elem_blue2
FROM product
, jsonb_array_elements(metadata #> '{options, opts}') WITH ORDINALITY arr(opt, ord)
WHERE opt ->> 'value' = 'blue';
Run Code Online (Sandbox Code Playgroud)
如果未找到匹配元素,则不返回任何内容。
WITH ORDNALITY
和elem_blue2
在这里不需要,但演示我们将在下一步中使用的技术。
更多解释:
2.当一个或多个售出时,如何更新“数量”(减去当前数量)?
从 Postgres 9.5 开始,有jsonb_set()
. 更新第一个元素的qty
键值:
UPDATE product
SET metadata = jsonb_set(metadata, '{options, opts, 0, qty}', '"29"', false)
WHERE ... -- some filter
Run Code Online (Sandbox Code Playgroud)
旁白:有什么理由将您的整数qty
存储为字符串 ( "30"
) 而不是数字 ( 30
) 吗?
为了更新“蓝色”元素,我们使用上面演示的技术确定数组索引,再加上一些UPDATE
使其完全动态的魔法:
UPDATE product p
SET metadata = jsonb_set(p.metadata, path, qty, false)
FROM product p1
, LATERAL ( -- move computations to subquery
SELECT ARRAY['options', 'opts', (ord - 1)::text, 'qty'] AS path -- fix off-by-one
, to_jsonb((opt ->> 'qty')::int - 1) AS qty -- subtract here!
FROM jsonb_array_elements(p1.metadata #> '{options, opts}') WITH ORDINALITY arr(opt, ord)
WHERE opt ->> 'value' = 'blue'
-- AND ... -- more filters
-- FOR UPDATE -- see below
) opt
WHERE p1.product_id = p.product_id -- use PK for match
Run Code Online (Sandbox Code Playgroud)
最后一个查询将一个数字写入“qty” ( '9'
),而不是字符串 ( '"9"'
),假设您已按照注释修复了它。
我们需要再次product
在FROM
子句中列出表以允许LATERAL
连接(否则这是不可能的) - 并自连接到它。
注意一个微小的竞争条件。在并发写负载很重的情况下,您可能希望在子查询 ( FOR UPDATE
) 中添加一个 lock 子句,以防止其他事务更改内部SELECT
和外部之间的行UPDATE
。看:
但你不应该,真的。如您所见,jsonb
(或任何与此相关的文档类型)不适合对单个属性进行定期更新。这很麻烦,而且相对昂贵。每次都必须写入包含完整文档新版本的新行。改用标准化的 DB 设计,只需重写相对较小的行,而其他部分和索引保持不变。
如果仅定期更新数量,您可以将其移至 1:n 表并将其合并到 JSON 文档中的VIEW
或MATERIALIZED VIEW
。重新设计超出了这个问题的范围。
归档时间: |
|
查看次数: |
8694 次 |
最近记录: |