Как выполнить операции обновления для столбцов типа 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).

есть два важных предостережения

  1. это заменит все свойства, называемые "name" в json (в случае, если у вас есть несколько свойств с тем же именем)
  2. это не так эффективно, как 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