Якщо JSONB — це чарівна скриня, куди ми складаємо дані, то || і jsonb_concat() — це інструменти, які дозволяють обʼєднувати такі скрині або змінювати їхній вміст. У реальному житті тобі може знадобитися обʼєднати кілька JSON-обʼєктів, додати дані з одного в інший або обʼєднати масиви в єдиний список.
Наприклад, уяви, що в тебе є два JSONB-обʼєкти:
{"name": "Alice", "age": 25}
і
{"city": "Wonderland", "hobbies": ["reading", "chess"]}
Ти хочеш отримати:
{"name": "Alice", "age": 25, "city": "Wonderland", "hobbies": ["reading", "chess"]}
Або обʼєднати два масиви JSONB:
[1, 2, 3]
і
[4, 5, 6]
щоб результат виглядав так:
[1, 2, 3, 4, 5, 6]
Все це робиться за допомогою || або jsonb_concat(). Давай розбиратись як.
Оператор || для обʼєднання JSONB
Оператор || дозволяє обʼєднувати два JSONB-обʼєкти або масиви в PostgreSQL. Він простий, швидкий і легкий у використанні. Ось основні правила його роботи:
- Якщо обʼєднуються два JSONB-обʼєкти, то результат міститиме ключі і значення обох обʼєктів.
- Якщо ключі співпадають, значення з правого операнда замінює значення з лівого.
- Якщо обʼєднуються JSONB-масиви, то елементи лівого і правого масиву обʼєднуються в один масив.
Приклад 1: Обʼєднання двох JSONB-обʼєктів
SELECT '{"name": "Alice", "age": 25}'::jsonb || '{"city": "Wonderland", "hobbies": ["reading", "chess"]}'::jsonb AS merged_object;
Результат:
{"name": "Alice", "age": 25, "city": "Wonderland", "hobbies": ["reading", "chess"]}
Приклад 2: Оновлення значень при співпадінні ключів
SELECT '{"name": "Alice", "age": 25}'::jsonb || '{"age": 30, "city": "Wonderland"}'::jsonb AS updated_object;
Результат:
{"name": "Alice", "age": 30, "city": "Wonderland"}
Зверни увагу, що значення ключа "age" з правого обʼєкта замінило значення з лівого.
Приклад 3: Обʼєднання масивів
SELECT '[1, 2, 3]'::jsonb || '[4, 5, 6]'::jsonb AS merged_array;
Результат:
[1, 2, 3, 4, 5, 6]
Функція jsonb_concat() для обʼєднання JSONB
Функція jsonb_concat() працює аналогічно оператору ||, але дає більше гнучкості, якщо треба використовувати її у функціях, тригерах або динамічних запитах. Вона приймає два аргументи типу JSONB і повертає обʼєднаний результат.
Приклад: використання jsonb_concat()
SELECT jsonb_concat('{"a": 1, "b": 2}'::jsonb, '{"b": 3, "c": 4}'::jsonb) AS combined;
Результат:
{"a": 1, "b": 3, "c": 4}
Обʼєднання обʼєктів і масивів: особливості та нюанси
При обʼєднанні обʼєктів зі співпадаючими ключами треба розуміти, що значення з правого обʼєкта мають пріоритет.
Наприклад:
SELECT '{"key1": "value1"}'::jsonb || '{"key1": "value2"}'::jsonb AS result;
Результат:
{"key1": "value2"}
Якщо ти хочеш уникнути заміни значень, краще зберігати такі дані в окремих ключах або використовувати інший підхід (наприклад, масив).
А от масиви завжди обʼєднуються шляхом додавання елементів з правого масиву до лівого. Наприклад:
SELECT '["a", "b"]'::jsonb || '["c", "d"]'::jsonb AS result;
Результат:
["a", "b", "c", "d"]
Якщо всередині масиву є обʼєкти, порядок обʼєктів зберігається:
SELECT '[{"id": 1}, {"id": 2}]'::jsonb || '[{"id": 3}]'::jsonb AS result;
Результат:
[{"id": 1}, {"id": 2}, {"id": 3}]
Практичні приклади
Оновлення профілю користувача. Припустимо, у нас є таблиця users, де профілі зберігаються у форматі JSONB:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
profile JSONB
);
INSERT INTO users (profile) VALUES ('{"name": "Alice", "age": 25}');
Тепер хочемо додати місто проживання:
UPDATE users
SET profile = profile || '{"city": "Wonderland"}'
WHERE id = 1;
Результат запиту:
{"name": "Alice", "age": 25, "city": "Wonderland"}
Обʼєднання даних про замовлення. Нехай у нас є таблиця orders:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
details JSONB
);
INSERT INTO orders (details) VALUES ('{"items": [{"product": "laptop", "quantity": 1}]}');
Тепер додаємо ще один продукт у замовлення:
UPDATE orders
SET details = jsonb_set(
details,
'{items}',
details->'items' || '[{"product": "mouse", "quantity": 2}]'::jsonb
)
WHERE id = 1;
Результат запиту:
{"items": [{"product": "laptop", "quantity": 1}, {"product": "mouse", "quantity": 2}]}
Відмінності між || і jsonb_concat()
Функціонально оператор || і функція jsonb_concat() ідентичні. Використовуй ||, якщо пишеш прості запити, бо запис коротший. Функція jsonb_concat() зручна, коли треба явно викликати її всередині програми або тригера.
Типові помилки і способи їх уникнення
Помилка: спроба обʼєднати несумісні типи.
SELECT '{"key": "value"}'::jsonb || '["value"]'::jsonb;
Результат:
ERROR: cannot concatenate jsonb objects and arrays
Тут зліва обʼєкт, а справа масив — PostgreSQL не може їх просто так обʼєднати. Щоб операція спрацювала, обидва операнди мають бути одного типу: або два обʼєкти, або два масиви.
Упущення: відсутність індексів при роботі з JSONB
Якщо ти часто фільтруєш дані по значеннях всередині JSONB-полів, а індексів нема — запити можуть дуже гальмувати. Це не помилка в класичному сенсі, але наслідки для продуктивності — дуже відчутні. Не забудь використовувати GIN-індекси:
CREATE INDEX idx_profile_data ON employees USING gin(profile);
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ