JSONB в PostgreSQL: документ внутри реляционной модели

2 мин чтения

Как сочетать обычные столбцы и JSONB в PostgreSQL и подобрать индекс под поиск по свойствам.

Постоянные поля и гибкие свойства
  1. Столбцы таблицыИдентификатор, связи и обязательные поля
  2. Документ JSONBХарактеристики с переменным набором ключей
  3. Фильтр и индексИндекс под оператор, проверка через EXPLAIN

У оборудования есть общие данные — серийный номер и модель, — а характеристики зависят от производителя. Сотня почти пустых столбцов усложняет таблицу, но переносить весь объект в JSON тоже неудобно. JSONB позволяет оставить постоянные поля обычными столбцами, а меняющийся набор свойств хранить рядом. Посмотрим, как это влияет на запросы и индексы.

Отделяем постоянное от переменного

CREATE TABLE devices (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  serial_no text NOT NULL UNIQUE,
  properties jsonb NOT NULL DEFAULT '{}'::jsonb
);
INSERT INTO devices(serial_no, properties)
VALUES ('LAB-001', '{"vendor":"Acme","channels":4}');
SELECT serial_no FROM devices
WHERE properties @> '{"vendor":"Acme"}'::jsonb;

Идентификатор и уникальность серийного номера остаются ограничениями таблицы. Это сохраняет понятную модель данных: JSONB дополняет схему, а не отменяет её. Перед добавлением произвольного поля определите, кто проверяет его тип и допустимые значения.

Индекс следует за запросом

CREATE INDEX devices_properties_gin ON devices USING gin(properties);
SELECT properties ->> 'vendor' AS vendor FROM devices;

GIN полезен для поддерживаемых им операторов, в том числе поиска по содержимому. Выражение с извлечением текста и сравнением — другая форма запроса; наличие GIN не означает, что любой фильтр по JSON автоматически ускорится. Для часто используемого выражения рассмотрите отдельный индекс и проверьте план на своих данных.

Когда лучше отдельный столбец

Если свойство участвует во внешних ключах, постоянно сортируется или имеет строгий бизнес-смысл, отдельный столбец обычно проще сопровождать. Сравните несколько типичных операций: запись целого документа, фильтрацию, отчёт и исправление ошибочного значения. Начните с небольшой модели и измерений, а не с переноса всех данных в JSON.

Добавьте устройства с разными свойствами и выполните поиск по производителю. Проверьте, что происходит, если поля нет. На маленькой таблице PostgreSQL может выбрать последовательное чтение вместо индекса — это нормальное решение планировщика, а не ошибка JSONB.

Источники и дальнейшее чтение