web-dev-qa-db-ja.com

Postgres 9.4でJSONB型の列に対して更新操作を実行する方法

Postgres 9.4データ型JSONBのドキュメントを見ると、JSONB列の更新方法がすぐにはわからない。

JSONBの型と機能に関する文書

http://www.postgresql.org/docs/9.4/static/functions-json.htmlhttp://www.postgresql.org/docs/9.4/static/datatype- json.html

例として、私はこの基本的なテーブル構造を持っています:

CREATE TABLE test(id serial, data jsonb);

次のように挿入は簡単です。

INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');

それでは、どのように 'data'列を更新するのでしょうか。これは無効な構文です。

UPDATE test SET data->'name' = 'my-other-name' WHERE id = 1;

これはどこかに文書化されていますが、私が見逃したことは明白ですか?ありがとう。

100
jvous

理想的には、リレーショナルデータベース内で操作したい構造化された通常のデータにJSON文書を使用しないでください。代わりに正規化リレーショナルデザインを使用してください。

JSONは主に、RDBMS内で操作する必要のない文書全体を保管することを目的としています。関連する

Postgresの行を更新すると、常にwhole行の新しいバージョンが書き込まれます。これが PostgresのMVCCモデル の基本原則です。パフォーマンスの観点からは、JSONオブジェクト内の単一のデータを変更するのか、それともそのすべてを変更するのかは、ほとんど関係ありません。新しいバージョンの行を作成する必要があります。

したがって マニュアルのアドバイス

JSONデータは、テーブルに格納されるときに他のデータ型と同じ同時実行制御の考慮事項に従います。大きな文書を保管することは実用的ですが、更新を行うと行全体に対して行レベルのロックがかかることに注意してください。更新トランザクション間のロック競合を減らすために、JSON文書を管理可能なサイズに制限することを検討してください。理想的には、JSONドキュメントはそれぞれ、ビジネスルールが指示するアトミックデータを表すべきであり、独立して変更できる小さなデータにさらに細分することはできません。

その要旨は、JSONオブジェクト内でanythingを変更するには、変更したオブジェクトを列に割り当てる必要があることです。 Postgresは、そのストレージ機能に加えてjsonデータを構築し操作するための限られた手段を提供します。ツールの数はバージョン9.2以降のすべての新しいリリースで大幅に増えました。しかし、原則は残ります:あなたalways完全に修正されたオブジェクトをカラムに割り当てる必要があり、Postgresは更新のたびに新しいローバージョンを書き込みます。

Postgres 9.3以降のツールを使った作業方法のいくつかのテクニック:

この回答はSO togetherに関する他のすべての回答と同じくらい多くのマイナス投票を集めています。人々はその考えを好まないようです:正規化されたデザインは非動的データより優れています。 Craig Ringerによるこの素晴らしいブログ投稿は、さらに詳しく説明しています。

35

あなたがPostgresql 9.5にアップグレードすることができるならば、他の人が言ったようにjsonb_setコマンドが利用可能です。

次の各SQL文では、簡潔にするためにwhere句を省略しました。明らかに、あなたはそれを付け加えたいと思うでしょう。

更新名:

UPDATE test SET data = jsonb_set(data, '{name}', '"my-other-name"');

タグを置き換えます(タグの追加や削除とは対照的に)。

UPDATE test SET data = jsonb_set(data, '{tags}', '["tag3", "tag4"]');

2番目のタグを置き換える(0から始まる):

UPDATE test SET data = jsonb_set(data, '{tags,1}', '"tag5"');

タグを追加(これは999タグより少ない限り有効です。引数999を1000以上に変更するとエラーが発生します。 Postgres 9.5.3では、これはもはや当てはまりません。もっと大きなインデックスを使うことができます。

UPDATE test SET data = jsonb_set(data, '{tags,999999999}', '"tag6"', true);

最後のタグを削除します。

UPDATE test SET data = data #- '{tags,-1}'

複雑な更新(最後のタグを削除し、新しいタグを挿入し、そして名前を変更する):

UPDATE test SET data = jsonb_set(
    jsonb_set(data #- '{tags,-1}', '{tags,999999999}', '"tag3"', true), 
    '{name}', '"my-other-name"');

これらの各例では、実際にはJSONデータの単一フィールドを更新していないことに注意することが重要です。代わりに、一時的な修正バージョンのデータを作成し、その修正バージョンを列に割り当てます。実際には、結果は同じになりますが、これを念頭に置いておくと、最後の例のように複雑な更新がわかりやすくなります。

複雑な例では、3つの変換と3つの一時バージョンがあります。最初に、最後のタグが削除されます。その後、そのバージョンは新しいタグを追加することによって変換されます。次に、2番目のバージョンはnameフィールドを変更して変換されます。 data列の値は最終バージョンに置き換えられます。

204
Jimothy

これは9.5で jsonb_set by Andrew Dunstan の形で存在しています jsonbx これは9.4で動作します

23

この問題に遭遇し、非常に素早い修正が欲しい(そして9.4.5以前で立ち往生している)人のために、これは私がしたことです:

テストテーブルの作成

CREATE TABLE test(id serial, data jsonb);
INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');

jsonbプロパティの名前を変更するための更新ステートメント

UPDATE test 
SET data = replace(data::TEXT,'"name":','"my-other-name":')::jsonb 
WHERE id = 1;

最終的に、受け入れられた答えは正しいものです(9.4.5以前では)jsonbオブジェクトの個々の部分を変更することはできません。ただし、jsonbオブジェクトを文字列(:: TEXT)にキャストしてから文字列を操作し、jsonbオブジェクト(:: jsonb)にキャストバックすることはできます。

2つの重要な注意点があります

  1. これは、jsonの "name"という名前のすべてのプロパティを置き換えます(同じ名前のプロパティが複数ある場合)。
  2. 9.5を使用している場合、これはjsonb_setほど効率的ではありません。

そうは言っても、私はjsonbオブジェクトのコンテンツのスキーマを更新しなければならないという状況に遭遇しました。これは、元の投稿者が求めていたことを正確に達成するための最も簡単な方法です。

13
Chad Capra

この質問はpostgres 9.4のコンテキストで行われましたが、この質問に来る新しいビューアは、postgres 9.5では、JSONBフィールドに対するサブドキュメントのCreate/Update/Delete操作は拡張なしでネイティブにサポートされていることに注意してください。関数。

参照してください。 JSONB修正演算子および関数

9
bguiz

私はPostgres 9.4で再帰的に動作する小さな関数を自分で書いた。私は同じ問題を抱えていました(Postgres 9.5でこの頭痛の種のいくつかは解決されました)。とにかくここに機能があります(私はそれがあなたのためにうまくいくことを願っています):

CREATE OR REPLACE FUNCTION jsonb_update(val1 JSONB,val2 JSONB)
RETURNS JSONB AS $$
DECLARE
    result JSONB;
    v RECORD;
BEGIN
    IF jsonb_typeof(val2) = 'null'
    THEN 
        RETURN val1;
    END IF;

    result = val1;

    FOR v IN SELECT key, value FROM jsonb_each(val2) LOOP

        IF jsonb_typeof(val2->v.key) = 'object'
            THEN
                result = result || jsonb_build_object(v.key, jsonb_update(val1->v.key, val2->v.key));
            ELSE
                result = result || jsonb_build_object(v.key, v.value);
        END IF;
    END LOOP;

    RETURN result;
END;
$$ LANGUAGE plpgsql;

これが使用例です。

select jsonb_update('{"a":{"b":{"c":{"d":5,"dd":6},"cc":1}},"aaa":5}'::jsonb, '{"a":{"b":{"c":{"d":15}}},"aa":9}'::jsonb);
                            jsonb_update                             
---------------------------------------------------------------------
 {"a": {"b": {"c": {"d": 15, "dd": 6}, "cc": 1}}, "aa": 9, "aaa": 5}
(1 row)

ご覧のとおり、詳細に分析し、必要に応じて値を更新/追加します。

5
J. Raczkiewicz

多分:UPDATEテストSETデータ= '"私の他の名前"' :: json WHERE id = 1;

データがJSON型である私の場合はうまくいきました。

3

Matheus de OliveiraはpostgresqlでJSON CRUD操作用の便利な関数を作成しました。それらは\ iディレクティブを使ってインポートすることができます。データ型がjsonbの場合は、関数のjsonbフォークに注目してください。

9.3 json https://Gist.github.com/matheusoliveira/9488951

9.4 jsonb https://Gist.github.com/inindev/2219dff96851928c2282

2
John Clark

'name'属性を更新します。

UPDATE test SET data=data||'{"name":"my-other-name"}' WHERE id = 1;

たとえば、 'name'および 'tags'属性を削除したい場合は、次のようにします。

UPDATE test SET data=data-'{"name","tags"}'::text[] WHERE id = 1;
1
Arthur