PostgreSQL 10データベースに「metadata」という名前のproduct
列を持つjsonb
テーブルがあります。ドキュメントとPostgresを扱うのは初めてです。 jsonb
の値は次のようになります。
{ "名前": "l33tシャツ"、 "価格": "1200"、 "数量": "60"、 "options": { "type": "radio"、 "title": "color"、 "opts": {"value": "red"、 "price": "-100"、 "qty": "30"}、 {"value": "blue"、 "price": "+200 "、" qty ":" 10 "}、 {" value ":" green "、" price ":" +300 "、" qty ":" 20 "} } }
2つの質問:
1. 「opts」配列の特定の要素を選択するにはどうすればよいですか?
select metadata->'options'->'opts'->(element here) from product
where metadata->'options'->'opts' @> '[{"value" : "blue"}]'
2. 1つ以上が販売されたときに「数量」を更新する方法(現在の「数量」から差し引く)
ガイド/メモへのさらなるリンクを歓迎します。
- 「opts」配列の特定の要素を選択するにはどうすればよいですか?
要素番号のインデックスを使用します マニュアルの指示に従って 。パスが長いほど、パスの表記は短くなります。
_SELECT metadata -> 'options' -> 'opts' -> 0 AS elem0
, metadata #> '{options, opts, 0}' AS elem0_path
FROM product;
_
n番目の要素を取得します。これは、質問のタイトルが尋ねるように(JSON配列インデックスは0で始まります)しかし、あなたの例は、実際に_"value":"blue"
_の要素が必要であることを示しています。それはささいなことではありません。 LATERAL
結合でjsonb_array_elements()
を使用してネストされた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';
_
一致する要素が見つからない場合は何も返しません。
_WITH ORDNALITY
_と_elem_blue2
_はここでは必要ありませんが、次のステップで使用する手法を示しています。
詳細な説明:
2.それらの1つ以上が販売されたときに「現在の数量から差し引く」「数量」を更新する方法
Postgres 9.5以降、 jsonb_set()
があります。 first要素のqty
キーの値を更新するには:
_UPDATE product
SET metadata = jsonb_set(metadata, '{options, opts, 0, qty}', '"29"', false)
WHERE ... -- some filter
_
余談:何らかの理由で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
_
最後のクエリは、numberを "qty"(_'9'
_)に書き込みますが、コメントとして修正した場合、文字列(_'"9"'
_)ではありません。
テーブルproduct
をFROM
句にもう一度リストして、LATERAL
結合(他の方法では不可能)を許可して、それに自己結合する必要があります。
小さな競合状態に注意してください。同時書き込みの負荷が高い場合、サブクエリ(_FOR UPDATE
_)にロック句を追加して、他のトランザクションが内部SELECT
と外部UPDATE
の間の行を変更しないようにすることができます。 。見る:
しかし、実際にはすべきではありません。ご覧のとおり、jsonb
(またはany文書型)は、単一の属性の定期的な更新には適していません。それは面倒で比較的高価です。完全なドキュメントの新しいバージョンを含む新しい行を毎回書き込む必要があります。代わりに、正規化されたDB設計を使用してください。比較的小さな行のみを書き換える必要があり、他の部分やインデックスは変更されません。
数量のみが定期的に更新される場合は、それを1:nテーブルに移動し、VIEW
または_MATERIALIZED VIEW
_のJSONドキュメントにマージします。再設計はこの質問の範囲を超えています。