Merging Concatenating JSON(B) columns in query
使用Postgres 9.4,我正在寻找一种方法来合并查询中的两个(或更多)
1 2 3 4 | id | json1 | json2 ---------------------------------------- 1 | {'a':'b'} | {'c':'d'} 2 | {'a1':'b2'} | {'f':{'g' : 'h'}} |
是否可以让查询返回以下内容:
1 2 3 4 | id | json ---------------------------------------- 1 | {'a':'b', 'c':'d'} 2 | {'a1':'b2', 'f':{'g' : 'h'}} |
不幸的是,我无法定义此处所述的功能。 这可能是"传统"查询吗?
在Postgres 9.5+中你可以像这样合并JSONB:
1 | SELECT json1 || json2; |
或者,如果它是JSON,则在必要时强制使用JSONB:
1 | SELECT json1::jsonb || json2::jsonb; |
要么:
1 | SELECT COALESCE(json1::jsonb||json2::jsonb, json1::jsonb, json2::jsonb); |
(否则,
例如:
1 2 3 4 | SELECT DATA || '{"foo":"bar"}'::jsonb FROM photos LIMIT 1; ?COLUMN? ---------------------------------------------------------------------- {"foo":"bar","preview_url":"https://unsplash.it/500/720/123"} |
感谢@MattZukowski在评论中指出这一点。
以下是可用于在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
这是我的测试表和数据
1 2 3 4 5 6 7 8 9 | 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"}}'); |
经过一些试验和错误
1 2 3 4 5 6 7 8 | 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的答案
您还可以将json转换为文本,连接,替换并转换回json。使用Clément提供的相同数据,您可以:
1 2 3 4 5 | SELECT REPLACE( (json1::text || json2::text), '}{', ', ')::json FROM test |
您还可以将所有json1连接成单个json:
1 2 3 4 5 6 7 | SELECT regexp_replace( array_agg((json1))::text, '}"(,)"{|\\| |^{"|"}$', '\1', 'g' )::json FROM test |
但是这个问题已经在前一段时间得到了回答;当
因此你可以在PostgreSQL 9.5中使用这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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列,其中
1 | SELECT id, jsonb_merge(json1, json2) FROM test |
仅供参考,如果有人在> = 9.5时使用jsonb并且他们只关心没有重复键合并的顶层元素,那么它就像使用||一样简单运营商:
1 2 3 4 5 | SELECT '{"a1":"b2"}'::jsonb || '{"f":{"g" :"h"}}'::jsonb; ?COLUMN? ----------------------------- {"a1":"b2","f": {"g":"h"}} (1 ROW) |
此函数将合并嵌套的json对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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$; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | CREATE OR REPLACE FUNCTION jsonb_merge(pCurrentData jsonb, pMergeData jsonb, pExcludeKeys text[]) RETURNS jsonb IMMUTABLE LANGUAGE SQL AS $$ SELECT json_object_agg(KEY,VALUE)::jsonb FROM ( WITH to_merge AS ( SELECT * FROM jsonb_each(pMergeData) ) SELECT * FROM jsonb_each(pCurrentData) WHERE KEY NOT IN (SELECT KEY FROM to_merge) AND ( pExcludeKeys ISNULL OR KEY <> ALL(pExcludeKeys)) UNION ALL SELECT * FROM to_merge ) t; $$; |
SELECT jsonb_merge('{"a":1,"b":9,"c":3,"e":5}':: jsonb,'{"b":2,"d":4}': :jsonb,'{"c","e"}':: text [])为jsonb
适用于||的替代方案当需要递归深度合并时(在此处找到):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 $$; |
试试这个,如果有人有合并两个JSON对象的问题
1 | SELECT TABLE.attributes::jsonb || json_build_object('foo',1,'bar',2)::jsonb FROM TABLE WHERE TABLE.x='y'; |