Postgres 9.4を使用して、クエリ内の2つ(またはそれ以上)の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 のように関数を定義することはできません。これは「伝統的な」クエリで可能ですか?
以下は、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
[〜#〜] edit [〜#〜]:PostgreSQL 9.5+の場合、以下のZubinの回答をご覧ください
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に感謝します。
また、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
ただし、この質問は、少し前にすでに回答されています。 json1
とjson2
が同じキーを含むとき。キーはドキュメントに2回表示されますが、 ベストプラクティス ではないようです。
したがって、uは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
参考までに、誰かが9.5以上でjsonbを使用していて、重複キーなしでマージされるトップレベルの要素のみに関心がある場合、||を使用するのと同じくらい簡単です。オペレーター:
select '{"a1": "b2"}'::jsonb || '{"f":{"g" : "h"}}'::jsonb;
?column?
-----------------------------
{"a1": "b2", "f": {"g": "h"}}
(1 row)
この関数はネストされた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$;
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として
||の代替としてうまく機能します再帰的なディープマージが必要な場合( 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
$$;
2つのJSONオブジェクトのマージに問題がある場合は、これを試してください
select table.attributes::jsonb || json_build_object('foo',1,'bar',2)::jsonb FROM table where table.x='y';