web-dev-qa-db-ja.com

オブジェクトのJSON配列にネストされた特定のキーのすべての値を更新します

Postgresテーブル_my_table_にjsonbdataがあります。次のデータが含まれています。

_[
   {"id":"1","status":"test status1","updatedAt":"1571145003"},
   {"id":"2","status":"test status2","updatedAt":"1571145323"}
]
_

1つのクエリを使用して、その配列内のすべてのオブジェクトのupdatedAtキーを更新したいと思います。私は試した:

_update my_table set data = data || '{"updatedAt": "1571150000"}';
_

上記のクエリは、次のように配列内に新しいオブジェクトを追加しました。

_[
   {"id":"1","status":"test status1","updatedAt":"1571145003"},
   {"id":"2","status":"test status2","updatedAt":"1571145323"},
   {"updatedAt":"1571150000"}
]
_

私は次のような出力が必要です:

_[
   {"id":"1","status":"test status1","updatedAt":"1571150000"},
   {"id":"2","status":"test status2","updatedAt":"1571150000"}
]
_

私もjsonb_set()を試しましたが、これには2番目のパラメーターを配列インデックスにする必要があります。配列内のJSONオブジェクトの数がわかりません。

これがカスタム関数で解決できる場合は、これも問題ありません。

1
kishram

最初に配列のすべての要素のネストを解除し、次に各要素を更新してから、単純に配列を構築する元のテーブルを更新します。

with ct as
(
    select id, jsonb_array_elements(data) dt
    from   t
)
, ct2 as
(
  select id, jsonb_set(dt, '{updatedAt}', '"1571150000"', false) dt2
  from   ct
)
update t
set    data = (select jsonb_agg(dt2) from ct2 where ct2.id = t.id);
select * from t;
 id |データ
-:| :------------------------------------------------- -------------------------------------------------- -------------------------------- 
 1 | [{"id": "1"、 "status": "test status1"、 "updatedAt": "1571150000"}、{"id": "2"、 "status": "test status2"、 "updatedAt": "1571150000"}] 

db <> fiddle ここ

1
McNets

McNetsが有効なソリューションを提供したPostgres 10を宣言しました。 CTEはPostgres 12より前にインライン化できないため、CTEを使用しない場合は少し効率的です。

_UPDATE tbl
SET    data = (
   SELECT jsonb_agg(jsonb_set(d, '{updatedAt}', '"15711500000"', false))
   FROM   jsonb_array_elements(data) d
   );
_

ただし、まだかなり非効率的ですが、実際にはすべて(またはほとんど)の行はneed更新ではありません。すべての行が更新されます-このテストの拡張テストケースで説明したように:

db <>フィドル ここ

すべての行の(小さな)部分だけが実際に更新を必要とする場合、それらのみを更新する方がmuchより効率的です。または、より適切には、それらの行を事前にインデックスサポートで特定します。 Postgres 10では、最初の部分は面倒で、2番目の部分は簡単ではありません(特殊な式のインデックスが必要になります)。

両方とも Postgres 12SQL/JSONパス言語

_UPDATE tbl
SET    data = (
   SELECT jsonb_agg(jsonb_set(dt, '{updatedAt}', '"15711500000"', false))
   FROM   jsonb_array_elements(data) dt
   )
WHERE  data @? '$.updatedAt ? (@ != "1571150000")';
_

追加されたWHERE data @? '$.updatedAt ? (@ != "1571150000")'は基本的に次のように述べています:

'最上位の(すべての配列要素の) "updatedAt"キーを見て、それらのいずれかが "1571150000"と等しくないかどうかを確認します。そのようなキーが見つかった場合(かつその場合のみ)にtrueを返します。 '

最も重要なのは、これによりUPDATEから適格でない行が早期に排除されることです。見る:

db <>フィドル ここ

canインデックスも使用します。 マニュアル:

また、GINインデックスは、jsonpathマッチングを実行する_@@_および_@?_演算子をサポートします。

ただし、_!=_は_==_よりもサポートがはるかに難しいため、この特定のケースではインデックスのサポートが制限されています。

0