如何合并两个JSON数组中的记录?

时间:2016-01-08 21:12:48

标签: json postgresql postgresql-9.4

我有两个Postgres SQL查询返回JSON数组:

Q 1:

[
  {"id": 1, "a": "text1a", "b": "text1b"},
  {"id": 2, "a": "text2a", "b": "text2b"},
  {"id": 2, "a": "text3a", "b": "text3b"},
  ...
 ]

Q2:

[
  {"id": 1, "percent": 12.50}, 
  {"id": 2, "percent": 75.00}, 
  {"id": 3, "percent": 12.50}
  ...
]

我希望结果是两个数组唯一元素的并集:

[
  {"id": 1, "a": "text1a", "b": "text1b", "percent": 12.50},
  {"id": 2, "a": "text2a", "b": "text2b", "percent": 75.00},
  {"id": 3, "a": "text3a", "b": "text3b", "percent": 12.50},
  ...
]

如何在Postgres 9.4中使用SQL?

2 个答案:

答案 0 :(得分:6)

假设数据类型jsonb并且您希望合并每个共享相同“id”值的JSON数组的记录。

Postgres 9.5

使新的concatenate operator || for jsonb values

更简单
SELECT json_agg(elem1 || elem2) AS result
FROM  (
   SELECT elem1->>'id' AS id, elem1
   FROM  (
      SELECT '[
        {"id":1, "percent":12.50}, 
        {"id":2, "percent":75.00}, 
        {"id":3, "percent":12.50}
       ]'::jsonb AS js
      ) t, jsonb_array_elements(t.js) elem1
   ) t1
FULL JOIN (
   SELECT elem2->>'id' AS id, elem2
   FROM  (
      SELECT '[
        {"id": 1, "a": "text1a", "b": "text1b", "percent":12.50},
        {"id": 2, "a": "text2a", "b": "text2b", "percent":75.00},
        {"id": 3, "a": "text3a", "b": "text3b", "percent":12.50}]'::jsonb AS js
      ) t, jsonb_array_elements(t.js) elem2
   ) t2 USING (id);

FULL [OUTER] JOIN确保您不会丢失其他数组中没有匹配的记录。

类型jsonb具有方便的属性,只保留记录中每个键的最新值。因此,结果中的重复“id”键会自动合并。

Postgres 9.5手册也建议:

  

注意:||运算符连接顶层的元素   每个操作数。它不会递归运行。例如,如果   两个操作数都是具有公共键字段名称的对象,值为   结果中的字段只是右手操作数的值。

Postgres 9.4

有点不方便。我的想法是提取数组元素,然后提取所有键/值对,UNION两个结果,聚合成每个id值的一个新的jsonb值,最后聚合成一个数组。

SELECT json_agg(j) -- ::jsonb
FROM  (
   SELECT json_object_agg(key, value)::jsonb AS j
   FROM  (
      SELECT elem->>'id' AS id, x.*
      FROM  (
         SELECT '[
           {"id":1, "percent":12.50}, 
           {"id":2, "percent":75.00}, 
           {"id":3, "percent":12.50}]'::jsonb AS js
         ) t, jsonb_array_elements(t.js) elem, jsonb_each(elem) x
      UNION ALL  -- or UNION, see below
      SELECT elem->>'id' AS id, x.*
      FROM  (
         SELECT '[
           {"id": 1, "a": "text1a", "b": "text1b", "percent":12.50},
           {"id": 2, "a": "text2a", "b": "text2b", "percent":75.00},
           {"id": 3, "a": "text3a", "b": "text3b", "percent":12.50}]'::jsonb AS js
         ) t, jsonb_array_elements(t.js) elem, jsonb_each(elem) x
      ) t
   GROUP  BY id
   ) t;

转换为jsonb会删除重复的键。或者,您可以使用UNION折叠重复项(例如,如果您希望json作为结果)。测试哪种情况更快。

相关:

答案 1 :(得分:1)

对于任何单个jsonb元素,这个concat ||运算符的使用对我来说非常适用于strip_nulls,另一个技巧是将结果转换回jsonb(不是数组)。

select jsonb_array_elements(jsonb_strip_nulls(jsonb_agg(
    '{
        "a" : "unchanged value",
        "b" : "old value",
        "d" : "delete me"
    }'::jsonb
    || -- The concat operator works as merge on jsonb, the right operand takes precedence
    -- NOTE: it only works one JSON level deep
    '{
        "b" : "NEW value",
        "c" : "NEW field",
        "d" : null
    }'::jsonb
)));

这给出了结果

 {"a": "unchanged value", "b": "NEW value", "c": "NEW field"}

正确输入jsonb