194

Postgres 9.4 データ型 JSONB のドキュメントを見てみると、JSONB 列を更新する方法がすぐにはわかりません。

JSONB の型と関数のドキュメント:

http://www.postgresql.org/docs/9.4/static/functions-json.html http://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"]}');

さて、「データ」列を更新するにはどうすればよいでしょうか? これは無効な構文です:

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

これは、私が見逃した明らかな場所に文書化されていますか? ありがとう。

4

9 に答える 9

456

Postgresql 9.5 にアップグレードできる場合はjsonb_set、他の人が言及しているように、コマンドを使用できます。

where以下の各 SQL ステートメントでは、簡潔にするために句を省略しました。明らかに、それを元に戻したいと思うでしょう。

更新名:

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

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

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

2 番目のタグ (0-indexed) を置き換える:

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 データの 1 つのフィールドを更新していないことに注意してください。代わりに、データの一時的な変更されたバージョンを作成し、その変更されたバージョンを列に割り当てます。実際には、結果は同じになるはずですが、これを念頭に置いておくと、最後の例のように複雑な更新がより理解しやすくなります。

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

于 2016-02-11T20:45:46.600 に答える
46

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

JSON は主に、RDBMS 内で操作する必要のないドキュメント全体を格納することを目的としています。関連している:

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

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

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

要点: JSON オブジェクト内の何かを変更するには、変更されたオブジェクトを列に割り当てる必要があります。Postgres はjson、そのストレージ機能に加えて、データを構築および操作する限定的な手段を提供します。ツールの武器は、バージョン 9.2 以降、新しいリリースごとに大幅に増加しています。しかし、原則は変わりません。完全に変更されたオブジェクトを常に列に割り当てる必要があり、Postgres は常に更新のために新しい行バージョンを書き込みます。

Postgres 9.3 以降のツールを使用するためのいくつかのテクニック:

この回答は、 SO に関する他のすべての回答と同じくらい多くの反対票を集めています。人々はこの考えを好まないようです: 正規化された設計は、通常のデータに対して優れています。Craig Ringer によるこの優れたブログ投稿では、さらに詳しく説明しています。

Craig や私と同様に Postgres の公式コントリビューターである Laurenz Albe による別のブログ投稿:

于 2014-11-02T21:28:49.187 に答える
17

この問題が発生し、非常に迅速な修正が必要な場合 (および 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,': "my-name"',': "my-other-name"')::jsonb 
WHERE id = 1;

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

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

  1. これにより、json 内の「my-name」に等しいすべての値が置き換えられます (同じ値を持つ複数のオブジェクトがある場合)。
  2. 9.5 を使用している場合、これは jsonb_set ほど効率的ではありません。
于 2016-04-01T15:06:11.467 に答える
12

この質問は、postgres 9.4 のコンテキストで尋ねられましたが、この質問に来る新しい閲覧者は、postgres 9.5 では、JSONB フィールドに対するサブドキュメントの作成/更新/削除操作が、拡張を必要とせずにデータベースによってネイティブにサポートされていることに注意する必要があります。機能。

参照: JSONB 変更演算子と関数

于 2015-08-06T01:42:26.703 に答える
7

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)

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

于 2016-10-13T12:05:18.803 に答える
2

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

于 2015-06-06T10:55:19.587 に答える