Работа с 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 для гибких данных, а ключевые поля держите в столбцах.
Проверьте себя
1. Чем оператор ->> отличается от -> при работе с JSONB?
AНичем
B-> возвращает значение как JSON, а ->> — как текст
C->> работает только с массивами
D-> устарел
2. Какой оператор проверяет, что JSONB-документ содержит указанный фрагмент?
A->
B->>
C@>
D||
3. Какой индекс ускоряет запросы по содержимому JSONB?
AB-tree
BGIN
CHash
DBRIN
Поддержать проект