如何修改PostgreSQL JSONB数据类型中的单个属性值?

时间:2017-01-27 20:13:51

标签: json postgresql jsonb

如何修改PostgreSQL JSONB数据类型中的单个字段?

假设我有一张叫做动物的桌子:

id       info
------------------------------------------------------------
49493   {"habit1":"fly","habit2":"dive","location":"SONOMA NARITE"}

我想简单地更改location属性的值(例如,大写或小写文本)。所以结果,在UPDATE之后是

    id       info
------------------------------------------------------------
49493   {"habit1":"fly","habit2":"dive","location":"sonoma narite"}

我在下面尝试了这个并且它不起作用

update animal set info=jsonb_set(info, '{location}', LOWER(info->>'location'), true) where id='49493';
----------------------------------
ERROR:  function jsonb_set(jsonb, unknown, text, boolean) does not exist
LINE 7: update animal set info=jsonb_set(info, '{con...
                                           ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
********** Error **********

ERROR: function jsonb_set(jsonb, unknown, text, boolean) does not exist

如果我只知道更新后的值是什么,那么我可以使用它:

update animal set info=jsonb_set(info, '{location}', '"sonoma narite"', true) where id='49493';

但是,如果文本值未知,我们只想做一些简单的操作,如追加,前置,大/小写,我不能简单地找到答案。

我很惊讶jsonb set函数不提供这样一个简单的操作,只是尝试更新jsonb中的text属性的情况。

有人可以帮忙吗?

2 个答案:

答案 0 :(得分:6)

jsonb_set()的第三个参数应为jsonb类型。问题是在将文本字符串转换为jsonb字符串时,您需要一个双引号的字符串。您可以使用concat()format()

update animal
set info = 
    jsonb_set(info, '{location}', concat('"', lower(info->>'location'), '"')::jsonb, true) 
--  jsonb_set(info, '{location}', format('"%s"', lower(info->>'location'))::jsonb, true) 
where id='49493'
returning *;

  id   |                               info                               
-------+------------------------------------------------------------------
 49493 | {"habit1": "fly", "habit2": "dive", "location": "sonoma narite"}
(1 row)

Postgres 9.4 中,您应该使用jsonb_each_text(),聚合键和值动态修改正确的值来取消json列,最后构建一个json对象:

update animal a
set info = u.info
from (
    select id, json_object(
        array_agg(key), 
        array_agg(
            case key when 'location' then lower(value)
            else value end))::jsonb as info
    from animal,
    lateral jsonb_each_text(info) 
    group by 1
    ) u
where u.id = a.id
and a.id = 49493;

如果你可以创建功能,这个解决方案可能会更愉快:

create or replace function update_info(info jsonb)
returns jsonb language sql as $$
    select json_object(
        array_agg(key), 
        array_agg(
            case key when 'location' then lower(value)
            else value end))::jsonb
    from jsonb_each_text(info)
$$

update animal
set info = update_info(info)
where id = 49493;

答案 1 :(得分:0)

我想分享一些好消息。感谢klin,他的意见帮助我发现了这个解决方案。在上面的例子中。如果我只是使用concat函数,那么我在klin发布的代码中找到的问题就解决了(简而言之,只有当文本值包含空格时才有效)。现在我可以小写一个属性值!

UPDATE test1 set info=jsonb_set(info, '{location}', concat('"',lower(info->>'locatioin'),'"')::jsonb, true) returning *;