在查询中合并连接JSON(B)列

Rob*_*bin 36 postgresql json jsonb postgresql-9.4

使用Postgres 9.4,我正在寻找一种方法来合并查询中的两个(或更多)jsonjsonb列.以下表为例:

  id | json1        | json2
----------------------------------------
  1   | {'a':'b'}   | {'c':'d'}
  2   | {'a1':'b2'} | {'f':{'g' : 'h'}}
Run Code Online (Sandbox Code Playgroud)

是否可以让查询返回以下内容:

  id | json
----------------------------------------
  1   | {'a':'b', 'c':'d'}
  2   | {'a1':'b2', 'f':{'g' : 'h'}}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法定义此处所述的功能.这可能是"传统"查询吗?

Zub*_*bin 53

在Postgres 9.5+中你可以像这样合并JSONB:

select json1 || json2;
Run Code Online (Sandbox Code Playgroud)

或者,如果它是JSON,则在必要时强制使用JSONB:

select json1::jsonb || json2::jsonb;
Run Code Online (Sandbox Code Playgroud)

要么:

select COALESCE(json1::jsonb||json2::jsonb, json1::jsonb, json2::jsonb);
Run Code Online (Sandbox Code Playgroud)

(否则,任何空值json1json2返回空行)

例如:

select data || '{"foo":"bar"}'::jsonb from photos limit 1;
                               ?column?
----------------------------------------------------------------------
 {"foo": "bar", "preview_url": "https://unsplash.it/500/720/123"}
Run Code Online (Sandbox Code Playgroud)

感谢@MattZukowski在评论中指出这一点.

  • 这实际上并不是深度合并..`SELECT'{"a":[1,2,3]}':: jsonb || '{"a":[4,5,6]}':: jsonb;`rhs中的键/ V对破坏了LHS. (12认同)
  • 美丽的。我喜欢 Postgres! (2认同)

Clé*_*ost 31

以下是可用于在PostgreSQL中创建json对象的内置函数的完整列表.http://www.postgresql.org/docs/9.4/static/functions-json.html

  • row_to_json并且json_objectdoest不允许您定义自己的密钥,因此不能在此处使用
  • json_build_object 希望你提前知道我们的对象将拥有多少个键和值,在你的例子中就是这种情况,但在现实世界中不应该这样.
  • json_object 看起来像是解决这个问题的好工具,但它迫使我们将我们的值转换为文本,所以我们也不能使用这个

嗯......好吧,我们不能使用任何经典功能.

让我们来看看一些集合函数,希望最好... http://www.postgresql.org/docs/9.4/static/functions-aggregate.html

json_object_agg是构建对象的唯一聚合函数,这是我们解决此问题的唯一机会.这里的技巧是找到正确的方式来提供json_object_agg功能.

这是我的测试表和数据

CREATE TABLE test (
  id    SERIAL PRIMARY KEY,
  json1 JSONB,
  json2 JSONB
);

INSERT INTO test (json1, json2) VALUES
  ('{"a":"b", "c":"d"}', '{"e":"f"}'),
  ('{"a1":"b2"}', '{"f":{"g" : "h"}}');
Run Code Online (Sandbox Code Playgroud)

经过一些试验和错误,json_object这里是一个查询,你可以用来合并PostgreSQL 9.4中的json1和json2

WITH all_json_key_value AS (
  SELECT id, t1.key, t1.value FROM test, jsonb_each(json1) as t1
  UNION
  SELECT id, t1.key, t1.value FROM test, jsonb_each(json2) as t1
)
SELECT id, json_object_agg(key, value) 
FROM all_json_key_value 
GROUP BY id
Run Code Online (Sandbox Code Playgroud)

编辑:对于PostgreSQL 9.5+,请看下面的Zubin的答案


小智 9

此函数将合并嵌套的 json 对象

create or replace function jsonb_merge(CurrentData jsonb,newData jsonb)
 returns jsonb
 language sql
 immutable
as $jsonb_merge_func$
 select case jsonb_typeof(CurrentData)
   when 'object' then case jsonb_typeof(newData)
     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
                   when e1.v = e2.v then e1.v 
                   else jsonb_merge(e1.v, e2.v)
                 end)
       from      jsonb_each(CurrentData) e1(k, v)
       full join jsonb_each(newData) e2(k, v) using (k)
     )
     else newData
   end
   when 'array' then CurrentData || newData
   else newData
 end
$jsonb_merge_func$;
Run Code Online (Sandbox Code Playgroud)

  • 这是我的 2 美分:我稍微修改了一下,只允许嵌套数组中的唯一值,只需替换 `CurrentData || 数组部分中的 newData 与 `array_to_json(array(select distinct jsonb_array_elements(CurrentData || newData)))::jsonb`。** TODO:** 不使用 JSON 而不是 JOSNB 作为中间体,为数组找到正确的函数 (2认同)

cai*_*ura 6

您还可以将json转换为文本,连接,替换并转换回json.使用Clément提供的相同数据,您可以:

SELECT replace(
    (json1::text || json2::text), 
    '}{', 
    ', ')::json 
FROM test
Run Code Online (Sandbox Code Playgroud)

您还可以将所有json1连接成单个json:

SELECT regexp_replace(
    array_agg((json1))::text,
    '}"(,)"{|\\| |^{"|"}$', 
    '\1', 
    'g'
)::json
FROM test
Run Code Online (Sandbox Code Playgroud)

  • 在Postgres 9.5中,假设您的列为JSONB,您可以执行`SELECT json1 || json2`合并JSON值. (6认同)

API*_*API 5

但是这个问题早在一段时间之前就已经回答了。when json1json2包含相同密钥的事实;该键在文档中出现两次,似乎不是最佳实践

因此,您可以jsonb_merge在PostgreSQL 9.5中使用此功能:

CREATE OR REPLACE FUNCTION jsonb_merge(jsonb1 JSONB, jsonb2 JSONB)
    RETURNS JSONB AS $$
    DECLARE
      result JSONB;
      v RECORD;
    BEGIN
       result = (
    SELECT json_object_agg(KEY,value)
    FROM
      (SELECT jsonb_object_keys(jsonb1) AS KEY,
              1::int AS jsb,
              jsonb1 -> jsonb_object_keys(jsonb1) AS value
       UNION SELECT jsonb_object_keys(jsonb2) AS KEY,
                    2::int AS jsb,
                    jsonb2 -> jsonb_object_keys(jsonb2) AS value ) AS t1
           );
       RETURN result;
    END;
    $$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

以下查询返回级联的jsonb列,其中的键json2占主导于json1

select id, jsonb_merge(json1, json2) from test
Run Code Online (Sandbox Code Playgroud)


小智 5

仅供参考,如果有人在 >= 9.5 中使用 jsonb 并且他们只关心在没有重复键的情况下合并顶级元素,那么就像使用 || 一样简单 操作员:

select '{"a1": "b2"}'::jsonb || '{"f":{"g" : "h"}}'::jsonb;
      ?column?           
-----------------------------
 {"a1": "b2", "f": {"g": "h"}}
(1 row)
Run Code Online (Sandbox Code Playgroud)


Flo*_*ian 5

看起来还没有人提出这种解决方案,所以这是我的看法,使用自定义聚合函数

create or replace aggregate jsonb_merge_agg(jsonb)
(
    sfunc = jsonb_concat,
    stype = jsonb,
    initcond = '{}'
);

create or replace function jsonb_concat(a jsonb, b jsonb) returns jsonb
    as 'select $1 || $2'
    language sql
    immutable
    parallel safe
;
Run Code Online (Sandbox Code Playgroud)

注意:这是使用||which 替换同一路径上的现有值,而不是深度合并它们。

现在jsonb_merge_agg可以像这样访问:

select jsonb_merge_agg(some_col) from some_table group by something;
Run Code Online (Sandbox Code Playgroud)