Работа с JSONB
Храним документы прямо в таблице и запрашиваем их содержимое операторами JSONB.
JSONB — бинарный тип PostgreSQL для хранения JSON-документов; он индексируется и позволяет делать запросы по содержимому прямо в SQL.
Зачем JSONB в реляционной базе
Иногда структура данных заранее неизвестна или меняется: настройки, метаданные, атрибуты товаров разных категорий. Заводить под каждое поле столбец неудобно. JSONB хранит весь документ в одном столбце, но при этом — в отличие от текстового хранения — позволяет искать и фильтровать по его полям и строить индексы.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT,
data JSONB
);
INSERT INTO products (name, data) VALUES
('Ноутбук', '{"brand": "Lenovo", "ram": 16, "tags": ["sale", "new"]}'),
('Телефон', '{"brand": "Samsung", "ram": 8, "tags": ["new"]}');
Операторы -> и ->>
Два главных оператора извлекают значения из JSON. Разница тонкая, но важная.
| Оператор | Что возвращает |
-> | значение как JSON (можно идти глубже) |
->> | значение как текст (для сравнений и вывода) |
-- -> возвращает JSON, -> вернёт "Lenovo" с кавычками (как json)
SELECT data -> 'brand' FROM products; -- "Lenovo"
-- ->> возвращает текст, удобно для WHERE и вывода
SELECT data ->> 'brand' FROM products; -- Lenovo
-- Достать вложенное / элемент массива
SELECT data -> 'tags' ->> 0 AS first_tag FROM products; -- sale
Фильтрация по JSON
Поскольку ->> даёт текст, его можно использовать в WHERE. А для проверки «содержит ли документ такой фрагмент» есть оператор @> (содержит).
-- Все товары бренда Lenovo
SELECT name FROM products
WHERE data ->> 'brand' = 'Lenovo';
-- Сравнение числового поля (приводим к int)
SELECT name FROM products
WHERE (data ->> 'ram')::int >= 16;
-- Документы, где есть тег "new" (оператор «содержит»)
SELECT name FROM products
WHERE data @> '{"tags": ["new"]}';
Обратите внимание: data ->> 'ram' возвращает текст, поэтому для числового сравнения мы приводим его к int через ::int.
Индексы GIN для JSONB
Чтобы запросы по содержимому JSONB были быстрыми на больших объёмах, для них существует специальный индекс — GIN.
-- Индекс под оператор @> и поиск по ключам
CREATE INDEX idx_products_data ON products USING GIN (data);
-- Теперь этот запрос пойдёт по индексу
SELECT name FROM products WHERE data @> '{"brand": "Lenovo"}';
Когда JSONB, а когда столбцы
- Столбцы — для важных, часто запрашиваемых и связываемых полей (цена, статус, дата). По ним удобнее ограничения и индексы.
- JSONB — для гибких, редко фильтруемых или разнородных данных (настройки, сырые ответы API, атрибуты).
Не превращайте JSONB в свалку для всего подряд — это уводит от преимуществ реляционной модели. JSONB хорош как дополнение, а не замена нормальной схеме.
Итог
JSONBхранит документы в столбце и позволяет искать по их содержимому в SQL.->возвращает JSON,->>— текст; оператор@>проверяет вложенность фрагмента.- Индекс
GINускоряет запросы по JSONB; используйте JSONB для гибких данных, а ключевые поля держите в столбцах.