Как выполнить операции обновления для столбцов типа JSONB в Postgres 9.4
просматривая документацию для Postgres 9.4 datatype JSONB, мне не сразу понятно, как делать обновления для столбцов JSONB.
документация для типов и функций JSONB:
http://www.postgresql.org/docs/9.4/static/functions-json.html http://www.postgresql.org/docs/9.4/static/datatype-json.html
в качестве примера, у меня есть эта базовая структура таблицы:
CREATE TABLE test(id serial, data jsonb);
вставить легко, как в:
INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');
Теперь, как бы я обновил столбец "данные"? Это недопустимый синтаксис:
UPDATE test SET data->'name' = 'my-other-name' WHERE id = 1;
это документировано где-то очевидно, что я пропустил? Спасибо.
8 ответов
В идеале вы не используете документы JSON для данных, которыми хотите управлять внутри реляционной базы данных. Используйте нормализованные реляционные конструкции.
JSON предназначен в первую очередь для хранения целых документов, которыми не нужно манипулировать внутри СУБД. По теме:
обновление строки в Postgres всегда записывает новую версию весь row. Это основной принцип модель MVCC Postgres. С точки зрения производительности вряд ли имеет значение, изменяете ли вы один фрагмент данных внутри объекта JSON или все: должна быть написана новая версия строки.
на советы по эксплуатации:данные JSON подвержены тем же соображениям управления параллелизмом, что и любой другой тип данных, хранящихся в таблице. Хотя хранить большой документы возможно, имейте в виду, что любое обновление приобретает блокировка уровня строки на всей строке. Рассмотрите возможность ограничения документов JSON на a управляемый размер для уменьшения конкуренции блокировки между обновлениями операции. В идеале документы JSON должны представлять собой атомарные datum, который диктуют бизнес-правила, не может быть разумно дальше подразделяется на более мелкие базы данных, которые могут быть изменены независимо.
суть этого: изменить что-нибудь внутри JSON объект, вы должны назначить измененный объект столбцу. Postgres поставляет ограниченные средства для создания и управления json
данные в дополнение к своим возможностям хранения. Арсенал инструментов существенно вырос с каждым новым выпуском начиная с версии 9.2. Но главное остается: Ты!--14-->всегда необходимо назначить полный измененный объект столбцу, и Postgres всегда записывает новую версию строки для любого обновления.
некоторые методы работы с инструментами Postgres 9.3 или более поздней версии:
если вы можете перейти на Postgresql 9.5, то jsonb_set
команда доступна, как упоминали другие.
в каждом из следующих операторов SQL я опустил where
предложение для краткости; очевидно, вы хотели бы добавить это обратно.
обновить имя:
UPDATE test SET data = jsonb_set(data, '{name}', '"my-other-name"');
заменить теги (в отличие от добавления или удаления тегов):
UPDATE test SET data = jsonb_set(data, '{tags}', '["tag3", "tag4"]');
замена второго тега (0-индексируется):
UPDATE test SET data = jsonb_set(data, '{tags,1}', '"tag5"');
добавить тег (этот будет работать до тех пор, пока существует менее 999 тегов; изменение аргумента 999 на 1000 или выше генерирует ошибку. Это больше не относится к Postgres 9.5.3; можно использовать гораздо больший индекс):
UPDATE test SET data = jsonb_set(data, '{tags,999999999}', '"tag6"', true);
удалить последний тег:
UPDATE test SET data = data #- '{tags,-1}'
сложные обновления (удалить последний тег, вставить новый тег, и изменить имя):
UPDATE test SET data = jsonb_set(
jsonb_set(data #- '{tags,-1}', '{tags,999999999}', '"tag3"', true),
'{name}', '"my-other-name"');
важно отметить, что в каждом из этих примеров вы фактически не обновляете одно поле данных JSON. Вместо этого вы создаете временную измененную версию данных и назначаете эту измененную версию обратно столбцу. На практике результат должен быть одинаковым, но с учетом этого следует сделать сложные обновления, как в последнем примере, более понятными.
в сложном примере есть три преобразования и три временные версии: Во-первых, удаляется последний тег. Затем эта версия преобразуется путем добавления нового тега. Следующий, второй версия преобразуется путем изменения
Это произошло в 9.5 в виде jsonb_set by Эндрю Данстан на основе существующего расширения jsonbx это работает с 9.4
этот вопрос был задан в контексте postgres 9.4, однако новые зрители, приходящие к этому вопросу, должны знать, что в postgres 9.5, операции создания/обновления/удаления вложенных документов в полях JSONB изначально поддерживаются базой данных без необходимости использования функций расширения.
посмотреть: JSONB изменение операторов и функций
для тех, кто сталкивается с этой проблемой и хочет очень быстро исправить (и застрял на 9.4.5 или ранее), вот что я сделал:
создание тестовой таблице
CREATE TABLE test(id serial, data jsonb);
INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}');
инструкция Update для изменения имени свойства jsonb
UPDATE test
SET data = replace(data::TEXT,'"name":','"my-other-name":')::jsonb
WHERE id = 1;
в конечном счете, принятый ответ верен в том, что вы не можете изменить отдельную часть объекта jsonb (в 9.4.5 или ранее); однако вы можете привести объект jsonb к строке (:: TEXT) и затем манипулируйте строкой и возвращайтесь к объекту jsonb (::jsonb).
есть два важных предостережения
- это заменит все свойства, называемые "name" в json (в случае, если у вас есть несколько свойств с тем же именем)
- это не так эффективно, как jsonb_set будет, если вы используете 9.5
С этим я столкнулся с ситуацией, когда мне пришлось обновить схему для содержимого в JSONB возражает, и это был самый простой способ выполнить именно то, что просил оригинальный плакат.
Я написал небольшую функцию для себя, которая рекурсивно работает в Postgres 9.4. У меня была такая же проблема (хорошо, что они решили часть этой головной боли в Postgres 9.5). Во всяком случае, вот функция (я надеюсь, что она хорошо работает для вас):
CREATE OR REPLACE FUNCTION jsonb_update(val1 JSONB,val2 JSONB)
RETURNS JSONB AS $$
DECLARE
result JSONB;
v RECORD;
BEGIN
IF jsonb_typeof(val2) = 'null'
THEN
RETURN val1;
END IF;
result = val1;
FOR v IN SELECT key, value FROM jsonb_each(val2) LOOP
IF jsonb_typeof(val2->v.key) = 'object'
THEN
result = result || jsonb_build_object(v.key, jsonb_update(val1->v.key, val2->v.key));
ELSE
result = result || jsonb_build_object(v.key, v.value);
END IF;
END LOOP;
RETURN result;
END;
$$ LANGUAGE plpgsql;
вот пример использования:
select jsonb_update('{"a":{"b":{"c":{"d":5,"dd":6},"cc":1}},"aaa":5}'::jsonb, '{"a":{"b":{"c":{"d":15}}},"aa":9}'::jsonb);
jsonb_update
---------------------------------------------------------------------
{"a": {"b": {"c": {"d": 15, "dd": 6}, "cc": 1}}, "aa": 9, "aaa": 5}
(1 row)
Как вы можете видеть, он анализирует глубоко и обновляет / добавляет значения, где это необходимо.
может: Обновить данные набора тестов = '"my-other-name"':: json, где id = 1;
Он работал с моим случаем, где данные являются типом json
Matheus de Oliveira создал удобные функции для операций JSON CRUD в postgresql. Их можно импортировать с помощью директивы \i. Обратите внимание на вилку JSONB функций, если JSONB, если ваш тип данных.
9.3 json https://gist.github.com/matheusoliveira/9488951
9.4 jsonb https://gist.github.com/inindev/2219dff96851928c2282