如何在Postgres 9.4中对JSONBtypes的列执行更新操作

仔细查看Postgres 9.4数据typesJSONB的文档,我不会立即明白如何在JSONB列上进行更新。

JSONBtypes和function的文档:

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; 

这是logging在某处明显,我错过了? 谢谢。

理想情况下,不要将JSON文档用于要在关系数据库中pipe理的数据。 请使用规范化的关系devise

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

在Postgres中更新行总是写入整个行的新版本。 这是Postgres的MVCC模型的基本原理。 从性能的angular度来看,在一个JSON对象中更改单个数据还是全部都是不重要的:必须编写一个新的行版本。

因此,手册中的build议 :

当存储在表中时,JSON数据受到与任何其他数据types相同的并发控制考虑。 尽pipe存储大型文档是可行的,但请记住,任何更新都会获得整行上的行级locking。 考虑将JSON文档限制为可pipe理的大小,以减less更新事务之间的锁争用。 理想情况下,JSON文档应该分别代表业务规则规定的primefaces数据,不能合理地将其细分为可以独立修改的较小的数据。

其要点:要修改JSON对象内的任何内容 ,必须将修改的对象分配给该列。 除了存储function之外,Postgres还提供有限的手段来构build和操作json数据。 从9.2版本开始,每个新版本都有大量的工具。 即将到来的版本9.4增加了很多 。

说明如何使用Postgres 9.3的工具:

  • 如何修改新的PostgreSQL JSON数据types中的字段?

如果您可以升级到Postgresql 9.5,则可以使用jsonb_set命令,如其他人所述。

在每个后面的SQL语句中,为了简洁,我省略了where子句。 显然,你想要添加回来。

更新名称:

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

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

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

replace第二个标签(0索引):

 UPDATE test SET data = jsonb_set(data, '{tags,1}', '"tag5"'); 

添加一个标签( 只要999个标签less于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数据的单个字段。 而是创build数据的临时修改版本,并将修改后的版本分配回列。 实际上,结果应该是一样的,但记住这一点应该使复杂的更新,如最后一个例子,更容易理解。

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

这是以jsonb_set的forms出现在9.5中的,由Andrew Dunstan根据现有的扩展jsonbx与9.4

对于那些遇到这个问题,想要一个非常快速的修复(并坚持在9.4.5或更早),这是我所做的:

创buildtesting表

 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对象转换为一个string(:: TEXT),然后操作string并将其转换回jsonb对象(:: jsonb)。

有两个重要的警告

  1. 这将取代json中名为“name”的所有属性(如果你有多个同名的属性)
  2. 这不如jsonb_set的效率,如果你使用9.5

就这样,我遇到了一个情况,我不得不更新jsonb对象中的内容模式,这是完成原始海报所要求的最简单的方法。

这个问题是在postgres 9.4的上下文中提出的,然而,新问题的观察者应该知道,在postgres 9.5中,JSONB字段的子文档Create / Update / Delete操作本地支持数据库,不需要扩展function。

请参阅: JSONB修改操作符和函数

我为Postgres 9.4recursion地编写了一个小函数。 我有同样的问题(他们在Postgres 9.5中解决了这个问题)。 无论如何,这里是function(我希望它适合你):

 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) 

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

也许:更新testing设置数据='“我的其他名称”:: JSON WHERE ID = 1;

它适用于我的情况,数据是jsontypes

Matheus de Oliveira为postgresql中的JSON CRUD操作创build了方便的函数。 可以使用\ i指令导入它们。 如果你的数据types是jsonb,注意函数的jsonb分支。

9.3 json https://gist.github.com/matheusoliveira/9488951

9.4 jsonb https://gist.github.com/inindev/2219dff96851928c2282