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命令可用,正如其他人所提到的。

在以下每个 SQL 语句中,为简洁起见,我省略了该where子句;显然,你想把它加回来。

更新名称:

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

替换标签(与添加或删除标签相反):

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

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

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

于 2016-02-11T20:45:46.600 回答
46

理想情况下,不要将 JSON 文档用于要在关系数据库中操作的结构化常规数据。请改用规范化关系设计

JSON 主要用于存储不需要在 RDBMS 中操作的整个文档。有关的:

在 Postgres 中更新一行总是写入行的新版本。这就是Postgres 的 MVCC 模型的基本原理。从性能的角度来看,您是更改 JSON 对象中的单个数据还是更改所有数据都无关紧要:必须编写该行的新版本。

因此手册中的建议

当存储在表中时,JSON 数据与任何其他数据类型一样受到相同的并发控制注意事项。尽管存储大文档是可行的,但请记住,任何更新都会获取整行的行级锁。考虑将 JSON 文档限制在可管理的大小,以减少更新事务之间的锁争用。理想情况下,每个 JSON 文档都应该代表一个原子数据,业务规则规定不能合理地进一步细分为可以独立修改的较小数据。

它的要点:要修改JSON 对象中的任何内容,您必须将修改后的对象分配给列。json除了存储能力之外,Postgres 还提供了有限的方法来构建和操作数据。自 9.2 版以来,随着每个新版本的发布,工具库都大幅增加。但是原则仍然存在:您总是必须将一个完整的修改对象分配给列,并且 Postgres 总是为任何更新写入一个新的行版本。

如何使用 Postgres 9.3 或更高版本的工具的一些技巧:

这个答案吸引了与我在 SO上的所有其他答案一样多的反对票。人们似乎不喜欢这个想法:规范化设计对于常规数据来说更胜一筹。Craig Ringer 的这篇出色的博客文章更详细地解释了:

另一位像 Craig 和我一样的Postgres 官方贡献者Laurenz Albe 的另一篇博文:

于 2014-11-02T21:28:49.187 回答
24

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

于 2015-07-08T21:10:20.343 回答
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)。

有两个重要的警告

  1. 这将替换 json 中所有等于“my-name”的值(如果您有多个具有相同值的对象)
  2. 如果您使用的是 9.5,这不如 jsonb_set 有效
于 2016-04-01T15:06:11.467 回答
15

更新“名称”属性:

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

如果您想删除例如“名称”和“标签”属性:

UPDATE test SET data=data-'{"name","tags"}'::text[] WHERE id = 1;
于 2018-11-13T11:54:48.803 回答
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 回答
5

也许: UPDATE test SET data = '"my-other-name"'::json WHERE id = 1;

它适用于我的情况,其中数据是 json 类型

于 2015-10-21T08:27:53.100 回答
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 回答