Слияние объединение формате JSON(Б) столбцы в запросе
используя 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'}}
к сожалению, я не могу определить функцию, как описано здесь. Возможно ли это с помощью "традиционного" запроса?
7 ответов
вот полный список встроенных функций, которые можно использовать для создания объектов json в PostgreSQL. http://www.postgresql.org/docs/9.4/static/functions-json.html
-
row_to_json
иjson_object
doest не позволяет вам определять свои собственные ключи, поэтому его нельзя использовать здесь -
json_build_object
ожидайте, что вы заранее узнаете, сколько ключей и значений будет иметь наш объект, это так в вашем примере, но не должно быть так в реальном мире -
json_object
выглядит как хороший инструмент для решения этой проблемы, но это заставляет нас бросить наши значения в текст, чтобы мы не могли использовать этот
хорошо... ОК, УО мы не можем использовать классические функции.
давайте посмотрим на некоторые агрегатные функции и надеемся на лучшее... http://www.postgresql.org/docs/9.4/static/functions-aggregate.html
json_object_agg
единственная агрегатная функция, которая строит объекты, это наша единственная шанс решить эту проблему. Фокус здесь в том, чтобы найти правильный способ кормить
в Postgres 9.5+ вы можете объединить JSONB следующим образом:
select json1 || json2;
или, если это JSON, принудить JSONB при необходимости:
select json1::jsonb || json2::jsonb;
например:
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,
'}"(,)"{|\| |^{"|"}$',
'',
'g'
)::json
FROM test
однако на этот вопрос ответили уже некоторое время назад; дело в том, что когда json1
и json2
содержат один и тот же ключ; ключ появляется дважды в документе, похоже, не лучшие практики.
поэтому u может использовать это 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;
следующий запрос возвращает объединенные столбцы jsonb, где ключи в json2
доминируют над клавишами в json1
:
select id, jsonb_merge(json1, json2) from test
FYI, если кто-то использует jsonb в >= 9.5, и они заботятся только о слиянии элементов верхнего уровня без дубликатов ключей, то это так же просто, как использовать оператор||:
select '{"a1": "b2"}'::jsonb || '{"f":{"g" : "h"}}'::jsonb;
?column?
-----------------------------
{"a1": "b2", "f": {"g": "h"}}
(1 row)
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;
$$;
выберите jsonb_merge ('{"a": 1, "b": 9, "c": 3, "e":5}':: jsonb,'{"b": 2, "d": 4}':: jsonb,'{"c","e"}':: text[]) как jsonb
эта функция объединит вложенные объекты 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$;