Многие знают, что в PostgreSQL есть тип данных JSONB. Но не все понимают, зачем он нужен в реляционной базе и какие задачи помогает решать.
Сегодня на примере тестовых таблиц интернет-магазина мы разберём:
-
где
JSONBвыигрывает у обычных колонок и массивов; -
как с ним работать эффективно;
-
основные операции: извлечение, фильтрация, обновление.
Исходная структура: категории и товары
У нас есть две таблицы.
Категории — с иерархической структурой (например, «Электроника» → «Смартфоны» → «Apple»).
Товары — с базовыми полями и массивом tags для хранения простых меток:
CREATE TABLE products ( id SERIAL PRIMARY KEY, name TEXT, category_id INT, tags TEXT[] );
Массив tags удобен для хранения простых меток: {'смартфон', 'apple', 'флагман'}.
Когда массивов достаточно (и даже хорошо)
Запрос 1: найти все товары с тегом «apple»
SELECT * FROM products WHERE 'apple' = ANY(tags);
Запрос 2: найти товары с тегами «смартфон» и «apple» одновременно
SELECT * FROM products WHERE tags @> ARRAY['смартфон', 'apple'];
Благодаря GIN-индексу такие запросы выполняются быстро даже на больших объёмах:
CREATE INDEX idx_products_tags ON products USING GIN(tags);
📌 Массивы — отличный инструмент для простых списков строк.
Когда массивов становится недостаточно
Представьте, что для тега «популярный» мы хотим хранить:
-
дату, когда товар стал популярным;
-
вес тега для поисковой выдачи.
Массив строк этого не умеет. Пришлось бы создавать отдельную таблицу product_tags с колонками tag, date, weight. Это усложняет схему и запросы.
Другой пример — характеристики товаров:
-
у смартфонов: процессор, память, объём батареи;
-
у наушников: время работы, тип подключения;
-
у одежды: размер, материал.
В классической реляционной модели это решается через:
-
таблицу атрибутов (
EAV); -
или множество колонок, большинство из которых будут
NULL.
Оба подхода — громоздкие и медленные.
Решение: JSONB
Добавим в таблицу товаров колонку properties типа JSONB:
ALTER TABLE products ADD COLUMN properties JSONB;
Теперь заполним её характеристиками в зависимости от категории.
Для смартфонов
UPDATE products SET properties = '{ "processor": "A17 Bionic", "ram": 8, "storage": 256, "battery": 4000, "water_resistance": "IP68" }' WHERE category_id = 1;
Для наушников
UPDATE products SET properties = '{ "battery_life": 30, "connection_type": "Bluetooth 5.3", "noise_cancelling": true }' WHERE category_id = 2;
Для книг
UPDATE products SET properties = '{ "author": "Лев Толстой", "pages": 1225, "genres": ["роман", "классика"] }' WHERE category_id = 3;
Основные операции с JSONB
1. Извлечение значения (оператор ->>)
SELECT name, properties ->> 'processor' AS cpu, properties ->> 'ram' AS memory FROM products WHERE properties ? 'processor';
2. Фильтрация по вложенному значению
Найти все товары с влагозащитой IP68:
SELECT * FROM products WHERE properties ->> 'water_resistance' = 'IP68';
3. Фильтрация по диапазону чисел
Найти смартфоны с батареей выше 4500 мАч:
SELECT * FROM products WHERE (properties ->> 'battery')::int > 4500;
📌 Обратите внимание на приведение типов: ->> возвращает текст.
Более эффективный вариант для фильтрации — использовать @> или создать индекс по выражению.
4. Поиск внутри массива, хранящегося в JSONB
Найти товары, у которых в жанрах указано «роман»:
SELECT * FROM products WHERE properties -> 'genres' ? 'роман';
Или любой из жанров:
SELECT * FROM products WHERE properties -> 'genres' ?| ARRAY['роман', 'основы'];
Изменение JSONB-данных без перезаписи всего объекта
PostgreSQL позволяет точечно обновлять JSONB.
Добавить новый цвет в массив colors
UPDATE products SET properties = jsonb_set( properties, '{colors}', COALESCE(properties -> 'colors', '[]'::jsonb) || '["черный"]'::jsonb ) WHERE id = 1;
Добавить новое поле
UPDATE products SET properties = properties || '{"warranty_months": 12}'::jsonb WHERE id = 1;
Удалить поле
UPDATE products SET properties = properties - 'warranty_months' WHERE id = 1;
Индексы для JSONB
Для ускорения запросов используйте GIN-индексы:
-- индекс для операторов ?, ?|, ?&, @> CREATE INDEX idx_products_properties ON products USING GIN(properties); -- индекс для конкретного пути (например, battery) CREATE INDEX idx_products_battery ON products ((properties ->> 'battery'));
Итог: когда использовать JSONB, а когда — нет
| Задача | Решение |
|---|---|
| Простой список строк (теги, метки) | TEXT[] + GIN |
| Ключ-значение с разными типами значений | JSONB |
| Динамические атрибуты (характеристики товаров) | JSONB |
| Вложенные структуры (объекты в объектах) | JSONB |
| Данные, которые нужно часто фильтровать и индексировать | JSONB + GIN |
| Строгая схема с фиксированными колонками | Обычные колонки INT, TEXT и т.д. |
Главное правило: JSONB не заменяет реляционную модель, а дополняет её там, где гибкость важнее строгости схемы.
Что мы сегодня разобрали
-
✅ Чем
JSONBотличается от массивов и когда его применять. -
✅ Как добавить
JSONBв существующую таблицу и заполнить данными. -
✅ Основные операции: извлечение, фильтрация, обновление.
-
✅ Индексация для быстрых запросов.
Статья подготовлена по материалам обучающего видео для вашего удобства.