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

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

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

Например,

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

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

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

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

Ответы [ 8 ]

13 голосов
/ 21 октября 2008

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

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.

8 голосов
/ 21 октября 2008

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

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

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

2 голосов
/ 21 октября 2008

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

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

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

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

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

1 голос
/ 21 октября 2008

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

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

tProduct 
  productID
  <other product details>

tCategory
  categoryID
  <other category details>

tProperty
  propertyID
  <other property details>

tProductXCategory
  productyID
  categoryID

tCategoryXProperty
  categoryID
  propertyID

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

Используйте хранимые процедуры или параметризованные запросы для повышения эффективности поиска.

1 голос
/ 21 октября 2008

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

0 голосов
/ 21 октября 2008

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

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

Товар имеет 1 * Категории

Товар имеет 1 * Опцию

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

после настройки вы можете использовать кеширование Nhibernate

Приветствия

0 голосов
/ 21 октября 2008

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

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

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

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

Categories(CategoryID, Name, Description, ..)

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

Раствор А.

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

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

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

A2. Соедините таблицы на основе отношений, чтобы использовать данные

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

Раствор Б.

Поддержка кошмара для различных типов свойств (например, int, varchar и т. Д.)

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

CategoryProperty (CPID, Name, Type)

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

PropertyAssociation (CPID, PropertyID)

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

Properties(CategoryID, PropertyID, Name, Type)

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

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

B4. Соедините все таблицы, чтобы получить желаемое свойство.

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

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

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

0 голосов
/ 21 октября 2008

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

  • product: ProductID
  • категория: CategoryID, ProductID
  • свойство: PropertyID, CategoryID

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

  • productCategoryPointer: ProdCatID, ProductID, CategoryID.

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

...