Как выполнить операции обновления для столбцов типа JSONB в Postgres 9.4
просматривая документацию для типа данных JSONB Postgres 9.4, мне не сразу понятно, как делать обновления для столбцов 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 всегда записывает новую версию весь строки. Это основной принцип модель MVCC Postgres. С точки зрения производительности вряд ли имеет значение, изменяете ли вы один фрагмент данных внутри объекта JSON или все это: новая версия строки должна быть записана.
на советы по эксплуатации:данные JSON подчиняются тем же соображениям управления параллелизмом, что и любой другой тип данных, хранящихся в таблице. Хотя хранить большой документов возможно, имейте в виду, что любое обновление приобретает блокировка на уровне строки по всей строке. Рассмотреть вопрос об ограничении JSON-документов в управляемый размер для того, чтобы уменьшить количество конфликтов блокировок между обновления операции. В идеале документы JSON должны представлять собой атомарные данные, которые диктуют бизнес-правила, не могут разумно быть дальше подразделяется на более мелкие данные, которые могут быть изменены независимо.
суть его: изменить что-нибудь внутри JSON объект, вы должны назначить измененный объект для столбца. Postgres поставляет ограниченные средства для создания и управления
json
данные в дополнение к своим возможностям хранения. Арсенал инструментов существенно вырос с каждым новым релизом начиная с версии 9.2. Но главное остается: Ты всегда необходимо назначить полный измененный объект столбцу, и 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-indexed):
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).
есть два важных предостережения
- это заменит все свойства, называемые " имя " в 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)
Как вы можете видеть, он анализирует глубоко вниз и обновляет/добавляет значения там, где это необходимо.
может быть: Обновить тестовый набор data = '"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