使用单个更新命令更新或创建嵌套的jsonb值

时间:2018-02-20 15:33:08

标签: sql json postgresql

我们说[在Postgres 9.6 ]中有一个名为xyz的JSONB列。在更新中,我想将此列的.foo.bar键设置为{"done":true}

但是更新必须容忍xyz的更新前价值是从{}

的任何内容
{ 
    "abc": "Hello"
}

或者

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

所以我不能马上使用jsonb_set,因为如果xyz->foo未定义则失败。在这种情况下,我可以使用jsonb_insert,但如果已经定义xyz->foo ,则会失败。

所以我尝试使用连接,例如

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

...在foo未定义时也会失败,因为xyz->'foo'null会覆盖连接中的{}

显然我可以编写一个使用if来解决这个问题的函数,但我真的觉得我应该能够在一次更新中完成它。

2 个答案:

答案 0 :(得分:8)

对于这个例子:

{ 
    "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
        }
    }
}

更新设置和插入

当对象存在时使用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子句。

答案 1 :(得分:0)

你可以使用||连接。它将覆盖或添加任何json值。

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

就这么简单。我很少使用我的软件中的功能。