如何在Postgres 9.4中对JSONB类型的列执行更新操作

时间:2014-11-02 19:37:24

标签: postgresql sql-update crud postgresql-9.4 jsonb

查看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;

这是否记录在某个明显错过的地方?谢谢。

9 个答案:

答案 0 :(得分:197)

如果您能够升级到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索引):

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列中的值将替换为最终版本。

答案 1 :(得分:33)

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

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

在Postgres中更新行总是会写一个新版本的整个行。这是Postgres' MVCC model的基本原则。从性能角度来看,无论是更改JSON对象中的单个数据还是更改所有数据都无关紧要:必须编写该行的新版本。

因此advice in the manual

  

JSON数据受到与以下相同的并发控制考虑   存储在表中时的任何其他数据类型。虽然存放大   文件是切实可行的,请记住,任何更新都会获得   整行上的行级锁定。考虑将JSON文档限制为   可管理的大小,以减少更新之间的锁争用   交易。理想情况下,JSON文档应该各自代表一个原子   业务规则所要求的数据不能合理地进一步发展   细分为可以独立修改的较小基准。

它的要点:要修改JSON对象中的任何,您必须将修改后的对象分配给该列。除了存储功能之外,Postgres还提供有限的方法来构建和操作json数据。自9.2版以来,每一个新版本的工具都在大幅增长。但是主体仍然存在:你总是必须为列分配一个完整的修改对象,Postgres总是为任何更新写一个新的行版本。

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

这个答案吸引了与SO 在一起的所有其他答案一样多的downvotes 。人们似乎不喜欢这个想法:标准化设计对于非动态数据来说是优越的。这篇由Craig Ringer撰写的优秀博客文章更详细地解释了:

答案 2 :(得分:23)

基于与9.4一起使用的现有扩展程序jsonb_set Andrew Dunstan jsonbx形式的9.5版本

答案 3 :(得分:13)

对于那些遇到这个问题并想要快速解决问题的人(并且仍然坚持使用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,'"name":','"my-other-name":')::jsonb 
WHERE id = 1;

最终,接受的答案是正确的,因为你不能修改jsonb对象的单个部分(在9.4.5或更早版本中);但是,您可以将jsonb对象强制转换为字符串(:: TEXT),然后操纵该字符串并强制转换回jsonb对象(:: jsonb)。

有两个重要的警告

  1. 这将取代所有名为" name"在json中(如果你有多个具有相同名称的属性)
  2. 如果使用9.5
  3. ,这不如jsonb_set有效

    话虽如此,我遇到了一种情况,我必须更新jsonb对象中内容的模式,这是完成原始海报所要求的最简单方法。

答案 4 :(得分:9)

这个问题是在postgres 9.4的背景下提出的, 然而,对于这个问题的新观众应该意识到在postgres 9.5中, 子文档数据库本身支持JSONB字段上的创建/更新/删除操作,无需扩展功能。

请参阅:JSONB modifying operators and functions

答案 5 :(得分:4)

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

正如您所见,它可以深入分析并在需要时更新/添加值。

答案 6 :(得分:3)

也许: 更新测试SET数据='" my-other-name"' :: json WHERE id = 1;

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

答案 7 :(得分: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

答案 8 :(得分:1)

更新“名称”属性:

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

,如果您想删除例如'name'和'tags'属性,

UPDATE test SET data=data-'{"name","tags"}'::text[] WHERE id = 1;