7

我有一些属于某个类别的产品。

每个类别可以有不同的属性。

例如,

  • 类别汽车具有属性颜色,功率,...
  • 类别宠物具有 重量年龄,...

类别数约为 10-15。每个类别中的属性数量为 3-15。产品数量非常大。

这个应用程序的主要要求是非常好的搜索。我们将选择类别,并为该类别中的每个属性输入标准。

必须为这种情况设计数据库。(SQL Server 2005)

4

8 回答 8

16

经典的设计方法是(星号表示主键列):

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 表中的更改的触发器进行更新。

于 2008-10-21T12:17:49.253 回答
8

如果应用程序的用户必须在搜索之前选择一个类别,我会按类别将您的产品分成不同的数据库表。类别本身几乎没有共同点这一事实也表明了这种解决方案。按类别细分也会使每次搜索速度更快,因为当您的用户正在寻找宠物时,不会浪费时间搜索汽车。

将产品划分为类别后,使用每个类别中产品的通用属性创建表格应该很容易。您的应用程序的用户界面应该是动态的(我正在考虑一个 Web 表单),因为当用户选择一个类别时,用户可以选择的属性应该会发生变化。

请注意,如果您希望在多个类别中列出产品,此解决方案将导致表中出现重复数据。在设计数据库时,需要在速度和规范化之间进行权衡。如果您没有适合多个类别的产品,那么我认为这将是最快的解决方案(就搜索速度而言)。

于 2008-10-21T12:02:12.467 回答
2

大多数人建议使用实体-属性-值 (EAV) 设计的变体。这种设计对您的情况来说太过分了,它引入了一大堆问题,例如:

  • 您不能为属性定义数据类型;您可以为整数属性输入“香蕉”
  • 您不能将属性声明为强制(即常规表中的 NOT NULL)
  • 您不能在属性上声明外键约束

如果您的类别较少,最好在 Bogdan Maxim 的答案中使用解决方案 A。也就是说,为所有类别定义一个具有公共属性的表 Products,并为每个类别定义一个附加表,以存储特定于类别的属性。

只有当您有无限数量的类别或者您必须在 Products 中的每行支持不同的属性集时,EAV 才是一个好的解决方案。但是你根本没有使用关系数据库,因为 EAV 违反了几个规范化规则。

如果您真的需要这么大的灵活性,最好将数据存储在 XML 中。实际上,您可能会研究 RDF 和Sesame 之类的语义 Web 框架。

于 2008-10-21T17:02:41.080 回答
1

您可能需要考虑一种实体-属性-值类型的排列,您可以在其中使用任意名称/值属性对“标记”每个产品。

于 2008-10-21T12:05:52.037 回答
1

你可以试试这个。我不太确定您问题的实际细节,也许有人可以帮助您翻译得更好一些。

5张桌子。3用于存储数据,2用于存储数据之间的映射。

tProduct 
  productID
  <other product details>

tCategory
  categoryID
  <other category details>

tProperty
  propertyID
  <other property details>

tProductXCategory
  productyID
  categoryID

tCategoryXProperty
  categoryID
  propertyID

您的查询需要使用映射表连接数据,但这将允许您在类别、属性和产品之间建立不同的多对多关系。

使用存储过程或参数化查询从搜索中获得更好的性能。

于 2008-10-21T12:08:16.947 回答
1

你可以尝试一些更面向对象的东西。

1. 为 Products 定义一个基表

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

2.定义表Categories

Categories(CategoryID, Name, Description, ..)

从这里你有很多选项,几乎所有选项都会破坏数据库的规范化。

解决方案 A。

如果您需要添加新产品,这将是一场维护噩梦

A1。为每个类别定义一个单独的表

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

A2。根据关系连接表以使用数据

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

解决方案 B。

不同类型属性(即 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)- 对于 int PropertyValueString(ProductID, CPID, PropertyID, Value)- 对于字符串 PropertyValueMoney(ProductID, CPID, PropertyID, Value)- 对于金钱

B4。加入所有表以检索所需的属性。

通过使用这种方法,您不必管理单独表中的所有属性,而是管理它们的值类型。基本上所有涉及的表都是查找表。缺点是,为了检索每个值,您必须对每个值类型进行“大小写”。

选择这些方法时,请记住这些文章(此处此处)。这个论坛帖子也很有趣,并且与主题相关,即使它是关于本地化的。

如果您觉得需要,您也可以使用Tomalak 的答案并添加强类型。

于 2008-10-21T13:03:47.710 回答
0

如果您想灵活处理类别和属性,则应创建以下表格:

  • 产品:产品ID
  • 类别:CategoryID、ProductID
  • 属性:PropertyID、CategoryID

当您想通过 mroe 共享一个类别而不是一个产品时,您必须为 n:m 连接创建一个链接表:

  • productCategoryPointer:ProdCatID、ProductID、CategoryID。

您将不得不在您的查询中加入一些连接,但是使用正确的索引,您应该能够快速查询您的数据。

于 2008-10-21T12:10:16.973 回答
0

我最近不得不这样做,我正在使用 NHibernate,我有三个实体

产品类别选项 OptionCategory

一个产品有 1* 个类别

一个产品有 1* 选项

一个选项有 1 个选项类别

一旦设置好,您就可以使用 Nhibernate 缓存

干杯

于 2008-10-21T13:09:01.427 回答