web-dev-qa-db-ja.com

単一の更新コマンドを使用してネストされたjsonb値を更新または作成する

xyzという名前のJSONB列が[Postgres 9.6]にあるとします。更新では、この列の.foo.barキーを{"done":true}に設定します。

ただし、更新では、xyzの更新前の値が{}から

{ 
    "abc": "Hello"
}

または多分

{ 
    "foo": {
        "baz": { "done": false }
    },
    "abc": "Hello"
}

jsonb_setが定義されていないと失敗するため、すぐにxyz->fooを使用することはできません。その場合、jsonb_insertを使用できますが、xyz->fooisが既に定義されている場合は失敗します。

だから私はのようなもので連結を使用しようとします

jsonb_set( 
    jsonb_set(xyz, '{foo}', '{}'::jsonb || xyz->'foo', true),
    '{foo, bar}', '{"done":true}', true
)

...xyz->'foo'fooであるため、nullが未定義の場合も失敗します。これは、連結で{}をオーバーライドします。

明らかに、これを回避するためにifを使用する関数を作成することもできますが、1回の更新で実行できるはずです。

9
Jesper We

この例では:

{ 
    "foo": {
        "baz": { "done": false }
    },
    "abc": "Hello"
}

挿入:

jsonb_insertを使用する必要があります。SELECTでテストできます。

SELECT jsonb_insert(xyz, '{foo,bar}', '{"done":true}'::jsonb) FROM tablename;

注:パスを正しく設定するには、jsonb_insertを使用することが非常に重要です。ここでのパスは '{foo:bar}'です。つまり、fooというオブジェクトbar内にJSONを挿入します。

したがって、結果は次のとおりです。

{
    "abc": "Hello",
    "foo": {
        "baz": {
            "done": false
        },
        "bar": {
            "done": true
        }
    }
}

セット:

barを編集してfalseに設定するには、jsonb_setを使用する必要があります。 SELECTでテストできます:

SELECT jsonb_set(xyz, '{foo,bar}', '{"done":false}'::jsonb) FROM tablename;

これは次を返します:

{
    "abc": "Hello",
    "foo": {
        "baz": {
            "done": false
        },
        "bar": {
            "done": false
        }
    }
}

SETおよびINSERTの更新

オブジェクトが存在する場合はjsonb_setを使用し、存在しない場合はjsonb_insertを使用します。どちらを使用するかを知らずに更新するには、CASEを使用できます

UPDATE tablename SET 
xyz= (CASE
        WHEN xyz->'foo' IS NOT NULL
        THEN jsonb_set(xyz, '{foo,bar}', '{"done":false}'::jsonb)
        WHEN xyz->'foo' IS NULL
        THEN jsonb_insert(xyz, '{foo}', '{"bar":{"done":true}}'::jsonb)
    END)
WHERE id=1;-- if you use an id to identify the JSON.

より具体的な値に対して、いくつかのCASE句を追加できます。

15
Dan

使用できます||連結する。 json値を上書きまたは追加します。

SELECT '{}'::jsonb || '{"foo":"bar"}'::jsonb
UPDATE tablename SET jdoc = jdoc || '{"foo":"bar"}'::jsonb

とても簡単です。ソフトウェアで関数を使用することはほとんどありません。

0
eatmeimadanish