Какова наилучшая практика решения этой проблемы (разные свойства для разных категорий)?

StackOverflow https://stackoverflow.com/questions/221584

Вопрос

У меня есть несколько продуктов, которые относятся к какой-то категории.

Каждая категория может иметь разные свойства.

Например,

  • категория легковые автомобили имеет свойства цвет, власть, ...
  • категория домашние питомцы иметь свойства масса, возраст, ...

Количество категорий около 10-15.Количество объектов в каждой категории от 3 до 15.Количество товаров очень большое.

Главное требование к этому приложению — очень хороший поиск.Мы выберем категорию и введем критерии для каждого объекта недвижимости в этой категории.

Необходимо спроектировать базу данных для этого сценария.(SQL-сервер 2005)

Это было полезно?

Решение

Классический подход к проектированию будет следующим (звездочка обозначает столбец первичного ключа):

Product
  ProductId*
  CategoryId: FK to Category.CategroyId
  Name

Category
  CategoryId*
  Name

Property
  PropertyId*
  Name
  Type

CategoryProperty
  CategoryId*: FK to Category.CategoryId
  PropertyId*: FK to Property.PropertyId

ProductProperty
  ProductId*: FK to Product.ProductId
  PropertyId*: FK to Property.PropertyId
  ValueAsString

Если вы можете смириться с тем фактом, что каждое значение свойства будет поступать в БД в виде строки, а информация о преобразовании типов будет храниться в таблице свойств, этого макета будет достаточно.

Запрос будет выглядеть примерно так:

SELECT
   Product.ProductId,
   Product.Name AS ProductName,
   Category.CategoryId,
   Category.Name AS CategoryName,
   Property.PropertyId,
   Property.Name AS PropertyName,
   Property.Type AS PropertyType,
   ProductProperty.ValueAsString
FROM
   Product 
   INNER JOIN Category         ON Category.CategoryId = Product.CategoryId
   INENR JOIN CategoryProperty ON CategoryProperty.CategoryId = Category.CategoryId
   INNER JOIN Property         ON Property.PropertyId = CategoryProperty.PropertyId
   INNER JOIN ProductProperty  ON ProductProperty.PropertyId = Property.PropertyId
                                  AND ProductProperty.ProductId = Product.ProductId
WHERE
   Product.ProductId = 1

Чем больше условий WHERE вы предоставите (вместе, напримериспользуя AND), тем быстрее будет запрос.Если вы правильно проиндексировали свои таблицы.

На самом деле это решение не является идеальным для ситуации полнотекстового индексирования.Здесь может помочь дополнительная таблица, в которой будет храниться весь текст, связанный с ProductId, в более денормализованном виде.Эту таблицу потребуется обновить с помощью триггеров, которые прослушивают изменения в таблице ProductProperty.

Другие советы

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

После того, как вы разделили продукты по категориям, вам будет легко создать таблицы, используя общие свойства продуктов в каждой категории.Пользовательский интерфейс вашего приложения должен быть динамичным (я имею в виду веб-форму), поскольку свойства, которые пользователь может выбирать, должны меняться, когда пользователь выбирает категорию.

Обратите внимание: если у вас есть продукты, которые вы хотите включить в несколько категорий, это решение приведет к дублированию данных в ваших таблицах.При проектировании базы данных существует компромисс между скоростью и нормализацией.Если вы не Если у вас есть продукты, которые подходят к нескольким категориям, то я думаю, что это будет самое быстрое решение (с точки зрения скорости поиска).

Большинство людей советуют использовать варианты конструкции Entity-Attribute-Value (EAV).Этот дизайн является излишним для вашей ситуации и создает целый ряд проблем, например:

  • Вы не можете определить тип данных для атрибута;вы можете ввести «банан» для целочисленного атрибута
  • Вы не можете объявить атрибут обязательным (т.НЕ NULL в обычной таблице)
  • Вы не можете объявить ограничение внешнего ключа для атрибута.

Если у вас небольшое количество категорий, лучше использовать решение А из ответа Богдана Максима.То есть определите одну таблицу Products с атрибутами, общими для всех категорий, и одну дополнительную таблицу для каждой категории для хранения атрибутов, специфичных для категории.

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

Если вам действительно нужна такая гибкость, лучше хранить данные в XML.Фактически, вы можете изучить RDF и семантические веб-фреймворки, такие как Кунжут.

Возможно, вы захотите рассмотреть Сущность-атрибут-значение тип договоренности, при котором вы можете «пометить» каждый продукт произвольными парами атрибутов имя/значение.

Вы можете попробовать это.Я не слишком уверен в деталях вашего вопроса, возможно, кто-нибудь поможет вам перевести немного лучше.

5 столов.3 для хранения данных, 2 для хранения сопоставлений между данными.

tProduct 
  productID
  <other product details>

tCategory
  categoryID
  <other category details>

tProperty
  propertyID
  <other property details>

tProductXCategory
  productyID
  categoryID

tCategoryXProperty
  categoryID
  propertyID

Ваши запросы должны будут объединить данные с помощью таблиц сопоставления, но это позволит вам иметь разные отношения «многие ко многим» между категориями, свойствами и продуктами.

Используйте хранимые процедуры или параметризованные запросы, чтобы повысить производительность поиска.

Если вы хотите гибко подходить к категориям и свойствам, вам следует создать следующие таблицы:

  • продукт:Идантификационный номер продукта
  • категория:Идентификатор категории, Идентификатор продукта
  • свойство:Идентификатор свойства, Идентификатор категории

если вы хотите поделиться категорией с несколькими продуктами, вам необходимо создать таблицу связей для объединения n:m:

  • ProductCategoryPointer:ProdCatID, ProductID, CategoryID.

Вам придется использовать некоторые соединения в ваших запросах, но с правильными индексами вы сможете быстро запрашивать данные.

Вы можете попробовать что-то более объектно-ориентированное.

1.Определите базовую таблицу для продуктов

Products(ProductID, CategoryID, <any other common properties>)

2.Определить категории таблицы

Categories(CategoryID, Name, Description, ..)

Отсюда у вас есть много вариантов, и почти все они нарушат нормализацию вашей базы данных.

Решение А.

Если вам понадобится добавить новые продукты, это будет кошмаром для обслуживания.

А1.Определите отдельную таблицу для каждой из категорий.

Cars(CarID, ProductID, ..) Pets(PetID, ProductID, ..)

А2.Объедините таблицы на основе отношений, чтобы использовать данные.

SELECT <fields> FROM Cars INNER JOIN Products ON Cars.ProductID = Products.ProductID

Решение Б.

Кошмар по техническому обслуживанию различных типов объектов (т.е.int, varchar и т. д.)

Б1.Определите таблицу для свойств

CategoryProperty (CPID, Name, Type)

БИ 2.Определите таблицу для хранения связей между категориями и свойствами.

PropertyAssociation (CPID, PropertyID)

Б12.Определите таблицу для хранения свойств (альтернатива для B1 и B2).

Properties(CategoryID, PropertyID, Name, Type)

Б3.Для каждого типа свойства (int, double, varchar и т. д.) добавьте таблицу значений.

PropertyValueInt(ProductID, CPID, PropertyID, Value) - для интPropertyValueString(ProductID, CPID, PropertyID, Value) - для струнPropertyValueMoney(ProductID, CPID, PropertyID, Value) - для денег

Б4.Объедините все таблицы, чтобы получить желаемое свойство.

Используя этот подход, вам не придется управлять всеми свойствами в отдельной таблице, а только типами их значений.По сути, все задействованные таблицы будут таблицами поиска.Недостаток заключается в том, что для получения каждого значения вам необходимо «Учитывать регистр» для каждого типа значения.

Обратите внимание на эти статьи (здесь и здесь) при выборе любого из этих подходов. Это сообщение на форуме тоже интересно и как-то связано с темой, хоть и про локализацию.

Вы также можете использовать Ответ Томалака и добавьте строгую типизацию, если чувствуете необходимость.

Недавно мне пришлось это сделать, и я использую NHibernate, где у меня есть три объекта.

Категория продукта Опция OptionCategory

У продукта есть 1* категории.

У продукта есть опция 1*

Опция имеет 1 OptionCategory

как только это будет настроено, вы сможете использовать кеширование Nhibernate.

Ваше здоровье

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top