WordPress создавался как движок блога. Позже его сделали универсальным: любая сущность (страница, запись, меню, а потом и товар) попадала в wp_posts с полем post_type. Магазин WooCommerce строился поверх готовой структуры. Это было удобно для плагинов: товары получают редактор, медиа, ревизии и комментарии по умолчанию.
Цена такого решения хорошо известна:
- В wp_posts лежат поля блога (post_content, post_excerpt, comment_status, ping_status), а у товара нужных ему полей (цена, остаток, артикул) в этой таблице нет.
- Все характеристики хранятся в wp_postmeta в виде строк (post_id, meta_key, meta_value). Значение там тип LONGTEXT, поэтому цена и диапазоны сравниваются как строки, а нормальные индексы по значениям построить нельзя.
- На больших каталогах это тормозит. WooCommerce сам это признал и добавил отдельную таблицу wc_product_meta_lookup с ценой, остатком и артикулом, чтобы фильтровать и сортировать быстрее. Заказы он тоже вынес из wp_posts в собственные таблицы (HPOS).
EAV-схема
Entity – Attribute – Value («сущность – атрибут – значение»)
Это способ хранить характеристики объекта не в отдельных колонках, а в виде строк: одна строка соответствует одному значению одной характеристики.
Обычная схема (колонки)
Таблица products
products id | name | power_kw | max_head | pump_type 1 | Джилекс 100 | 0.75 | 40 | центробежный 2 | Водолей 3 | 0.30 | 72 | вибрационный
Каждая характеристика является колонкой. Это быстро и понятно, но у разных категорий разные характеристики. Если держать всё в колонках, таблица быстро обрастает десятками пустых полей. Новая характеристика требует менять структуру таблицы (ALTER TABLE).
products product_attribute_values
id | name product_id | attribute | value
1 | Джилекс 100 1 | power_kw | 0.75
2 | Водолей 3 1 | max_head | 40
1 | pump_type | центробежный
2 | power_kw | 0.30
2 | max_head | 72
Плюсы и минусы
Плюсы:
- Гибкость: у каждого товара свой набор характеристик, без пустых колонок.
- Новые атрибуты добавляются без миграций.
- Фильтры можно строить динамически из данных.
Минусы:
- Запросы сложнее. Чтобы получить товары с мощностью до 1 кВт и напором от 30 м, нужно несколько соединений или EXISTS.
- Если хранить все значения как текст (как wp_postmeta в WooCommerce), нельзя нормально сравнивать числа и строить индексы по значениям.
- Нет ограничений базы на уровне полей, поэтому проверять данные нужно в коде.