EAV (Entity-Attribute-Value) - зберігати довільні атрибути не колонками, а рядками «сутність - атрибут - значення»:
product_attributes: product_id | attribute | value
1 | color | red
1 | weight | 1.2
2 | voltage | 220
Привабливо для товарів з різними характеристиками (одяг має розмір, а холодильник - об'єм): нові атрибути додаються без зміни схеми. Так влаштований, наприклад, Magento.
Чому EAV уникають:
- Немає типів:
value- текст для всього. Числа порівнюються як рядки ('10' < '9'), дати - теж, і будь-яке значення можна записати в будь-який атрибут. - Немає обмежень:
NOT NULL, діапазони, зовнішні ключі для значень - неможливо описати базою. - Запити жахливі: «червоні товари вагою до 2 кг» - кілька самоз'єднань тієї самої таблиці чи
GROUP BYз умовами вHAVING. Кожен новий фільтр - ще одинJOIN. - Продуктивність падає з ростом таблиці атрибутів, а індекси допомагають слабо.
- Звіти й аналітика вимагають «розвертати» рядки в колонки.
Альтернативи:
jsonbдля змінних атрибутів - найчастіша сучасна заміна:
CREATE TABLE products (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
price numeric(10, 2) NOT NULL, -- спільні поля - звичайні колонки
attributes jsonb NOT NULL DEFAULT '{}'
);
SELECT * FROM products WHERE attributes @> '{"color": "red"}';
Зберігаються типи JSON (числа - числами), є GIN-індекси, документ читається одним рядком.
- Окремі таблиці для типів сутностей (наслідування таблиць: спільна
products+product_clothing,product_appliances), коли типів небагато і їхні атрибути стабільні. - Звичайні колонки - якщо атрибутів насправді десяток і вони відомі заздалегідь.
Коли EAV ще має сенс: атрибути визначають самі користувачі в адмінці, їх тисячі, і потрібна метаінформація про кожен атрибут (тип, одиниці, порядок показу). Тоді EAV роблять з типізованими колонками значень (value_int, value_text, value_date) і таблицею-описом атрибутів.