浏览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;

这是否被记录在我遗漏的某个地方?谢谢。


当前回答

如果你能够升级到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"]');

替换第二个标记(0-index):

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数据的单个字段。相反,您将创建数据的临时修改版本,并将修改后的版本分配回列。在实践中,结果应该是相同的,但是记住这一点可以使复杂的更新(如上一个例子)更容易理解。

在这个复杂的示例中,有三个转换和三个临时版本:首先,删除最后一个标记。然后,通过添加一个新标记来转换该版本。接下来,通过更改名称字段转换第二个版本。将data列中的值替换为最终版本。

其他回答

对于那些遇到这个问题并想要快速修复的人(并且被困在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)。

这里有两个重要的警告

这将替换json中所有等于“my-name”的值(在这种情况下,您有多个具有相同值的对象) 如果您使用的是9.5,这就没有jsonb_set那么高效了

如果你能够升级到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"]');

替换第二个标记(0-index):

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数据的单个字段。相反,您将创建数据的临时修改版本,并将修改后的版本分配回列。在实践中,结果应该是相同的,但是记住这一点可以使复杂的更新(如上一个例子)更容易理解。

在这个复杂的示例中,有三个转换和三个临时版本:首先,删除最后一个标记。然后,通过添加一个新标记来转换该版本。接下来,通过更改名称字段转换第二个版本。将data列中的值替换为最终版本。

这将在9.5中以jsonb_set的形式出现,由Andrew Dunstan基于现有扩展jsonbx实现,jsonbx可以在9.4中使用

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

我为自己写了一个小函数,在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)

正如你所看到的,它深入分析并在需要的地方更新/添加值。