使用Postgres 9.4,我正在寻找一种方法来合并查询中的两个(或更多)json
或jsonb
列。以下表为例:
id | json1 | json2
----------------------------------------
1 | {'a':'b'} | {'c':'d'}
2 | {'a1':'b2'} | {'f':{'g' : 'h'}}
是否可以让查询返回以下内容:
id | json
----------------------------------------
1 | {'a':'b', 'c':'d'}
2 | {'a1':'b2', 'f':{'g' : 'h'}}
不幸的是,我无法定义here所描述的功能。这可能是传统的"查询?
答案 0 :(得分:43)
在Postgres 9.5+中你可以像这样合并JSONB:
select json1 || json2;
或者,如果它是JSON,必要时强制使用JSONB:
select json1::jsonb || json2::jsonb;
或者:
select COALESCE(json1::jsonb||json2::jsonb, json1::jsonb, json2::jsonb);
(否则,json1
或json2
中的任何空值都会返回一个空行)
例如:
select data || '{"foo":"bar"}'::jsonb from photos limit 1;
?column?
----------------------------------------------------------------------
{"foo": "bar", "preview_url": "https://unsplash.it/500/720/123"}
感谢@MattZukowski在评论中指出这一点。
答案 1 :(得分:30)
以下是可用于在PostgreSQL中创建json对象的内置函数的完整列表。 http://www.postgresql.org/docs/9.4/static/functions-json.html
row_to_json
和json_object
不允许您定义自己的密钥,因此无法在此处使用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"}}');
经过一些试验和错误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
编辑:对于PostgreSQL 9.5+,请看下面的Zubin答案
答案 2 :(得分:6)
此外,您可以将json转换为文本,连接,替换并转换回json。使用Clément提供的相同数据,您可以:
SELECT replace(
(json1::text || json2::text),
'}{',
', ')::json
FROM test
您还可以将所有json1连接成单个json:
SELECT regexp_replace(
array_agg((json1))::text,
'}"(,)"{|\\| |^{"|"}$',
'\1',
'g'
)::json
FROM test
答案 3 :(得分:4)
然而这个问题已经在前一段时间得到了回答;当json1
和json2
包含相同的密钥时;密钥在文档中出现两次,似乎不是best practice。
因此你可以在PostgreSQL 9.5中使用这个jsonb_merge
函数:
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;
以下查询返回连接的jsonb列,其中json2
中的键比json1
中的键占优势:
select id, jsonb_merge(json1, json2) from test
答案 4 :(得分:4)
仅供参考,如果有人在> = 9.5中使用jsonb并且他们只关心没有重复键合并的顶级元素,那么它就像使用||一样简单操作者:
select '{"a1": "b2"}'::jsonb || '{"f":{"g" : "h"}}'::jsonb;
?column?
-----------------------------
{"a1": "b2", "f": {"g": "h"}}
(1 row)
答案 5 :(得分:3)
此函数将合并嵌套的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$;
答案 6 :(得分:1)
看起来还没有人提出这种解决方案,所以这是我的看法,使用custom aggregate functions:
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
;
注意:这是使用
||
来替换同一路径中的现有值,而不是深度合并它们。
现在可以像这样访问jsonb_merge_agg
:
select jsonb_merge_agg(some_col) from some_table group by something;
答案 7 :(得分:0)
let fbLoginBtn = FBSDKLoginButton()
SELECT jsonb_merge(' {" a":1," b":9," c":3," e&# 34;:5}' :: jsonb,' {" b":2," d":4}' :: jsonb,&# 39; {" c"," e"}' :: text [])as jsonb
答案 8 :(得分:0)
如果有人在合并两个JSON对象方面遇到问题,请尝试此操作
select table.attributes::jsonb || json_build_object('foo',1,'bar',2)::jsonb FROM table where table.x='y';
答案 9 :(得分:0)
可以很好地替代||需要递归深度合并(找到here)时:
create or replace function jsonb_merge_recurse(orig jsonb, delta jsonb)
returns jsonb language sql as $$
select
jsonb_object_agg(
coalesce(keyOrig, keyDelta),
case
when valOrig isnull then valDelta
when valDelta isnull then valOrig
when (jsonb_typeof(valOrig) <> 'object' or jsonb_typeof(valDelta) <> 'object') then valDelta
else jsonb_merge_recurse(valOrig, valDelta)
end
)
from jsonb_each(orig) e1(keyOrig, valOrig)
full join jsonb_each(delta) e2(keyDelta, valDelta) on keyOrig = keyDelta
$$;