Хранение динамического набора атрибутов в базе данных
Периодически возникает потребность хранить динамические наборы определенных атрибутов для объектов. Например, в электронном магазине нужно хранить карточку товара, где набор атрибутов может быть разный для разных видов товара. Телефоны, например, имеют размер, вес, поддерживаемые стандарты связи, цветной/черно-белый дисплей; Книги – кол-во страниц, автор, издательство, isbn. Существует несколько способов хранить атрибуты, самые популярные: хранить товар и его атрибуты в общей таблице и шаблон EAV.
С хранением аттрибутов в одной общей таблице все более-менее понятно, а что такое шаблон EAV? Вот его упрощенный вариант (нет указаний о типе атрибута). Создаем пять таблиц: для храненния товаров (items), для хранения групп атрибутов (types), для хранения названий атрибутов (attributes), таблцу для связи групп атрибутов и самих атрибутов (attributes_types) и таблицу для значений атрибутов (values)
`item_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`attribute_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`value` tinytext,
PRIMARY KEY (`product_id`,`attribute_id`)
) TYPE=MyISAM;
CREATE TABLE `attributes_types` (
`attribute_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
`type_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`attribute_id`,`type_id`)
) TYPE=InnoDB;
CREATE TABLE `attributes` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(126) NOT NULL DEFAULT '',
`abbr` varchar(24) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) TYPE=InnoDB;
CREATE TABLE `types` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) TYPE=InnoDB;
CREATE TABLE `items` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`type_id` int(11) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) TYPE=InnoDB;
Для выборки одного объекта с атрибутами используем два запроса, например, ищем объект с id =6
SELECT i.*, t.name type_name
FROM `items` i, `types` t
WHERE i.id = 6 AND i.type_id = t.id
# Другой для выборки его атрибутов
# (значение type_id берем из результатов первого запроса)
SELECT a.name, v.value, v.abbr
FROM `attributes_types` at, `attributes` a, `values` v
WHERE at.attribute_id = a.id AND at.type_id = 1 AND a.id = v.attribute_id AND v.item_id = 6
Для поиска по атрибутам, используем этот запрос:
# по которым осуществляется поиск,
# а v.value - искомые значения
SELECT i.*, v.value, v.abbr, a.name
FROM `items` i, `attributes` a, `values` v
WHERE a.id = v.attribute_id AND p.id = v.item_id AND ((a.id = 1 AND v.value = '100') OR (a.id = 2 AND v.value = '50'))

Спасибо, с этой заметки больше понятно, чем с Википедии. Попробую делать такое с использованием Doctrine.
Xobb
9 Ноя 09 at 16:22
Еще взгляните как EAV в Magento реализовали. Много интересного можна подсмотреть.
Xobb
9 Ноя 09 at 16:25
Спасибо, посмотрю. Как раз в тему.
Кстати, еще, наверное, можно использовать MongoDB. В mySQL хранить ключи и описания наборов данных, а сами динамические атрибуты хранить в MongoDB. На днях собираюсь попробовать такой вариант.
undr
10 Ноя 09 at 17:54
Мне в Битриксе на эту тему очень нравились инфоблоки, на рельсах кто-нибудь подобное бы реализовал, зачетная штучка бы была.
woto
14 июня 10 at 1:26