Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在PostgreSQL中合并JSONB值?_Json_Postgresql_Jsonb - Fatal编程技术网

在PostgreSQL中合并JSONB值?

在PostgreSQL中合并JSONB值?,json,postgresql,jsonb,Json,Postgresql,Jsonb,使用| |运算符可产生以下结果: select '{"a":{"b":2}}'::jsonb || '{"a":{"c":3}}'::jsonb ; ?column? ----------------- {"a": {"c": 3}} (1 row) 我希望能够做到以下几点??只是操作员的占位符: select '{"a":{"b":2}}'::jsonb ?? '{"a":{"c":3}}'::jsonb ; ?column? -------------

使用| |运算符可产生以下结果:

select '{"a":{"b":2}}'::jsonb || '{"a":{"c":3}}'::jsonb ;
    ?column?     
-----------------
 {"a": {"c": 3}}
(1 row)
我希望能够做到以下几点??只是操作员的占位符:

select '{"a":{"b":2}}'::jsonb ?? '{"a":{"c":3}}'::jsonb ;
    ?column?     
-----------------
 {"a": {"b": 2, "c": 3}}
(1 row)
因此,您可以看到顶级a键合并了其子值,结果同时包含b和c

如何在Postgres中深度合并两个JSONB值

这可能吗?如果可能,怎么可能

更复杂的测试用例:

select '{"a":{"b":{"c":3},"z":true}}'::jsonb ?? '{"a":{"b":{"d":4},"z":false}}'::jsonb ;
    ?column?     
-----------------
 {"a": {"b": {"c": 3, "d": 4}, "z": false}}
(1 row)
原语与对象合并的另一个测试用例:

select '{"a":{"b":{"c":3},"z":true}}'::jsonb ?? '{"a":{"b":false,"z":false}}'::jsonb ;
        ?column?         
-----------------
 {"a": {"b": false, "z": false}}
(1 row)

PostgreSQL 9.5之后,您可以使用jsonb_set函数:

“{a,c}”查看路径,如果该路径不存在,则将创建该路径。 “{a:{c:3}}”:jsonb>“{a,c}”这将得到c的值 如果create_missing为true,则添加新_值默认为true

这是一份文件

一次合并多个属性:

with jsonb_paths(main_part,missing_part) as (
values ('{"a":{"b":2}}','{"a":{"c":3,"d":4}}')
)
select jsonb_object_agg(t.k,t.v||t2.v)
from jsonb_paths,
jsonb_each(main_part::jsonb) t(k,v),
jsonb_each(missing_part::jsonb) t2(k,v);

result: {"a":{"c":3,"b":2,"d":4}}

对于这两个值,您应该分别使用jsonb_合并未列出的元素。在一个非平凡的查询中执行此操作可能会让人不舒服,因此我更喜欢这样一个自定义函数:

create or replace function jsonb_my_merge(a jsonb, b jsonb)
returns jsonb language sql as $$
    select 
        jsonb_object_agg(
            coalesce(ka, kb), 
            case 
                when va isnull then vb 
                when vb isnull then va 
                else va || vb 
            end
        )
    from jsonb_each(a) e1(ka, va)
    full join jsonb_each(b) e2(kb, vb) on ka = kb
$$;
使用:

您可以使用递归稍微修改该函数,以获得适用于任何嵌套级别的解决方案:

create or replace function jsonb_recursive_merge(a jsonb, b jsonb)
returns jsonb language sql as $$
    select 
        jsonb_object_agg(
            coalesce(ka, kb), 
            case 
                when va isnull then vb 
                when vb isnull then va 
                when jsonb_typeof(va) <> 'object' then va || vb
                else jsonb_recursive_merge(va, vb)
            end
        )
    from jsonb_each(a) e1(ka, va)
    full join jsonb_each(b) e2(kb, vb) on ka = kb
$$;
最后,OP提出的功能变体变更见以下注释:

create or replace function jsonb_recursive_merge(a jsonb, b jsonb) 
returns jsonb language sql as $$ 
select 
    jsonb_object_agg(
        coalesce(ka, kb), 
        case 
            when va isnull then vb 
            when vb isnull then va 
            when jsonb_typeof(va) <> 'object' or jsonb_typeof(vb) <> 'object' then vb 
            else jsonb_recursive_merge(va, vb) end 
        ) 
    from jsonb_each(a) e1(ka, va) 
    full join jsonb_each(b) e2(kb, vb) on ka = kb 
$$;

根据您的用例,这种深度合并可以有完全不同的解释。为了完整起见,我的直觉通常规定以下规则:

object+object:每个属性都存在于每个对象中,不在另一个对象中。如果明确提到JSON的空值,则认为它在对象中。当一个属性同时存在于两个对象中时,合并将以通常约定的相同规则递归地继续进行。 数组+数组:结果是两个数组的串联。 array+primitive/object:结果是第一个数组,附加第二个JSON值。 任何其他情况:结果是第二个JSON值so f.ex。原语或不兼容类型相互覆盖。 这个函数的额外好处是,可以使用任何类型的JSON值调用它:始终生成一个结果&从不抱怨JSON值类型


这可能会有帮助:有趣的是,我没有想过使用jsonb_集函数。H/w这意味着我需要知道/指定需要合并的路径,而b一次只能合并一个属性。尝试了您添加了更复杂值的第二个查询:使用jsonb_pathsmain_part,缺少_part作为值“{a:{b:{c:3},x:5}”,“a:{b:{d:4},y:6}”从jsonb___路径中选择jsonb_对象_aggt.k,t.v|t2.v,jsonb_每个主要部分::jsonb tk,v,jsonb_每个部分::jsonb t2k,v;结果是{a:{b:{d:4},x:5,y:6}结果我想要的是{a:{b:{c:3,d:4},x:5,y:6}。。。所以它看起来只处理一个层次的深度。对,如果一个或多个父级不存在,jsonb_集将无法工作。我认为它只会在父对象是一个对象并且键还不存在的情况下创建一个键;它返回的结果是{a:{b:{d:4},x:5,y:6},我希望它返回的是{a:{b:{c:3,d:4},x:5,y:6}。。。实际上,我刚刚遇到了一个中断的场景:selectjsonb_recursive_merge'{a:{b:{c:3},z:true}}':jsonb'{a:{b:{d:4},z:false}}':jsonb;产生{a:{b:{c:3,d:4},z:[真,假]}。。。但是我希望它不会创建那个数组,而是产生以下结果:{a:{b:{c:3,d:4},z:false}}创建或替换函数jsonb_recursive_mergea jsonb,b jsonb返回jsonb语言sql作为$$select jsonb_object_agg coalesceka,kb,当va为空时,vb为空时,va为空时,jsonb_类型的va为“对象”,然后vb为其他jsonb_递归的(合并),vb从jsonb_eacha e1ka结束,va完全连接jsonb_eachb e2kb,vb为ka=kb$$;你觉得这个怎么样?这是一样的,除了当type不等于object,然后va | | vb变成vb,然后vb,我觉得没关系。您可以自由修改功能以满足您的期望。您的变体似乎非常符合逻辑,可能比原来的更符合逻辑。顺便说一句,我发现了另一种边缘情况,其中一个对象被一个原语合并,请参见qn更新创建或替换函数jsonb_recursive_mergea jsonb,b jsonb返回jsonb语言sql作为$$select jsonb_object_agg coalesceka,kb,当va为空时,vb为空时,va为空时,jsonb_类型的va为“对象”或jsonb_类型的vb为“对象”,然后vb为其他类型的jsonb_递归合并,vb从jsonb_eacha e1ka结束,va完全连接jsonb_eachb e2kb,vb为ka=kb$$;需要对功能进行微小更改^
create or replace function jsonb_recursive_merge(a jsonb, b jsonb)
returns jsonb language sql as $$
    select 
        jsonb_object_agg(
            coalesce(ka, kb), 
            case 
                when va isnull then vb 
                when vb isnull then va 
                when jsonb_typeof(va) <> 'object' then va || vb
                else jsonb_recursive_merge(va, vb)
            end
        )
    from jsonb_each(a) e1(ka, va)
    full join jsonb_each(b) e2(kb, vb) on ka = kb
$$;
select jsonb_recursive_merge( 
    '{"a":{"b":{"c":3},"x":5}}'::jsonb, 
    '{"a":{"b":{"d":4},"y":6}}'::jsonb);

             jsonb_recursive_merge              
------------------------------------------------
 {"a": {"b": {"c": 3, "d": 4}, "x": 5, "y": 6}}
(1 row)

select jsonb_recursive_merge(
    '{"a":{"b":{"c":{"d":{"e":1}}}}}'::jsonb, 
    '{"a":{"b":{"c":{"d":{"f":2}}}}}'::jsonb)

            jsonb_recursive_merge             
----------------------------------------------
 {"a": {"b": {"c": {"d": {"e": 1, "f": 2}}}}}
(1 row)
create or replace function jsonb_recursive_merge(a jsonb, b jsonb) 
returns jsonb language sql as $$ 
select 
    jsonb_object_agg(
        coalesce(ka, kb), 
        case 
            when va isnull then vb 
            when vb isnull then va 
            when jsonb_typeof(va) <> 'object' or jsonb_typeof(vb) <> 'object' then vb 
            else jsonb_recursive_merge(va, vb) end 
        ) 
    from jsonb_each(a) e1(ka, va) 
    full join jsonb_each(b) e2(kb, vb) on ka = kb 
$$;
create or replace function jsonb_merge_deep(jsonb, jsonb)
  returns jsonb
  language sql
  immutable
as $func$
  select case jsonb_typeof($1)
    when 'object' then case jsonb_typeof($2)
      when 'object' then (
        select    jsonb_object_agg(k, case
                    when e2.v is null then e1.v
                    when e1.v is null then e2.v
                    else jsonb_merge_deep(e1.v, e2.v)
                  end)
        from      jsonb_each($1) e1(k, v)
        full join jsonb_each($2) e2(k, v) using (k)
      )
      else $2
    end
    when 'array' then $1 || $2
    else $2
  end
$func$;