有条件地替换 jsonb 列中每行的单个值

Dmi*_*try 3 sql postgresql replace sql-update jsonb

我需要一种更有效的方法来更新 Postgres 9.5 中单个表的行。我目前正在使用 pg_dump 执行此操作,并在 Linux 操作系统环境中的搜索和替换操作后使用更新的值重新导入。

table_a有 300000 行,2 列:id bigintjson_col jsonb. json_col有大约 30 个键:“C1”到“C30”,如本例所示:

Table_A

    id,json_col
    1  {"C1":"Paris","C2":"London","C3":"Berlin","C4":"Tokyo", ... "C30":"Dallas"}
    2  {"C1":"Dublin","C2":"Berlin","C3":"Kiev","C4":"Tokyo", ... "C30":"Phoenix"}
    3  {"C1":"Paris","C2":"London","C3":"Berlin","C4":"Ankara", ... "C30":"Madrid"}
    ...
Run Code Online (Sandbox Code Playgroud)

要求是批量搜索从 C1 到 C30 的所有键,然后在它们中查找值“柏林”并替换为“马德里”,并且仅当马德里不重复时。即 id:1 与密钥 C3,和 id:2 与 C2。id:3 将被跳过,因为 C30 已经存在这个值

它必须在 PostgreSQL 9.5 中的单个 SQL 命令中,一次并考虑jsonb列中的所有键。

kli*_*lin 5

最快最简单的方法是将列修改为文本:

update table_a
set json_col = replace(json_col::text, '"Berlin"', '"Madrid"')::jsonb
where json_col::text like '%"Berlin"%'
and json_col::text not like '%"Madrid"%'
Run Code Online (Sandbox Code Playgroud)

这是一个实用的选择。上面的查询与其说是修改对象属性,不如说是查找和替换操作(如在文本编辑器中)。第二种选择更复杂,而且肯定要贵得多。即使使用快速的 Javascript 引擎(下面的示例),更正式的解决方案也会慢很多倍。

你可以试试Postgres Javascript

create extension if not exists plv8;

create or replace function replace_item(data jsonb, from_str text, to_str text)
returns jsonb language plv8 as $$
    var found = 0;
    Object.keys(data).forEach(function(key) {
        if (data[key] == to_str) {
            found = 1;
        }
    })
    if (found == 0) {
        Object.keys(data).forEach(function(key) {
            if (data[key] == from_str) {
                data[key] = to_str;
            }
        })
    }
    return data;
$$;

update table_a
set json_col = replace_item(json_col, 'Berlin', 'Madrid');
Run Code Online (Sandbox Code Playgroud)