我有一些属于某个类别的产品。
每个类别可以有不同的属性。
例如,
- 类别汽车具有属性颜色,功率,...
- 类别宠物具有 重量,年龄,...
类别数约为 10-15。每个类别中的属性数量为 3-15。产品数量非常大。
这个应用程序的主要要求是非常好的搜索。我们将选择类别,并为该类别中的每个属性输入标准。
必须为这种情况设计数据库。(SQL Server 2005)
我有一些属于某个类别的产品。
每个类别可以有不同的属性。
例如,
类别数约为 10-15。每个类别中的属性数量为 3-15。产品数量非常大。
这个应用程序的主要要求是非常好的搜索。我们将选择类别,并为该类别中的每个属性输入标准。
必须为这种情况设计数据库。(SQL Server 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 表中的更改的触发器进行更新。
如果应用程序的用户必须在搜索之前选择一个类别,我会按类别将您的产品分成不同的数据库表。类别本身几乎没有共同点这一事实也表明了这种解决方案。按类别细分也会使每次搜索速度更快,因为当您的用户正在寻找宠物时,不会浪费时间搜索汽车。
将产品划分为类别后,使用每个类别中产品的通用属性创建表格应该很容易。您的应用程序的用户界面应该是动态的(我正在考虑一个 Web 表单),因为当用户选择一个类别时,用户可以选择的属性应该会发生变化。
请注意,如果您希望在多个类别中列出产品,此解决方案将导致表中出现重复数据。在设计数据库时,需要在速度和规范化之间进行权衡。如果您没有适合多个类别的产品,那么我认为这将是最快的解决方案(就搜索速度而言)。
大多数人建议使用实体-属性-值 (EAV) 设计的变体。这种设计对您的情况来说太过分了,它引入了一大堆问题,例如:
如果您的类别较少,最好在 Bogdan Maxim 的答案中使用解决方案 A。也就是说,为所有类别定义一个具有公共属性的表 Products,并为每个类别定义一个附加表,以存储特定于类别的属性。
只有当您有无限数量的类别或者您必须在 Products 中的每行支持不同的属性集时,EAV 才是一个好的解决方案。但是你根本没有使用关系数据库,因为 EAV 违反了几个规范化规则。
如果您真的需要这么大的灵活性,最好将数据存储在 XML 中。实际上,您可能会研究 RDF 和Sesame 之类的语义 Web 框架。
您可能需要考虑一种实体-属性-值类型的排列,您可以在其中使用任意名称/值属性对“标记”每个产品。
你可以试试这个。我不太确定您问题的实际细节,也许有人可以帮助您翻译得更好一些。
5张桌子。3用于存储数据,2用于存储数据之间的映射。
tProduct
productID
<other product details>
tCategory
categoryID
<other category details>
tProperty
propertyID
<other property details>
tProductXCategory
productyID
categoryID
tCategoryXProperty
categoryID
propertyID
您的查询需要使用映射表连接数据,但这将允许您在类别、属性和产品之间建立不同的多对多关系。
使用存储过程或参数化查询从搜索中获得更好的性能。
你可以尝试一些更面向对象的东西。
Products(ProductID, CategoryID, <any other common properties>)
Categories(CategoryID, Name, Description, ..)
从这里你有很多选项,几乎所有选项都会破坏数据库的规范化。
如果您需要添加新产品,这将是一场维护噩梦
Cars(CarID, ProductID, ..)
Pets(PetID, ProductID, ..)
SELECT <fields> FROM Cars INNER JOIN Products ON Cars.ProductID = Products.ProductID
不同类型属性(即 int、varchar 等)的维护噩梦
CategoryProperty (CPID, Name, Type)
PropertyAssociation (CPID, PropertyID)
Properties(CategoryID, PropertyID, Name, Type)
PropertyValueInt(ProductID, CPID, PropertyID, Value)
- 对于 int
PropertyValueString(ProductID, CPID, PropertyID, Value)
- 对于字符串
PropertyValueMoney(ProductID, CPID, PropertyID, Value)
- 对于金钱
通过使用这种方法,您不必管理单独表中的所有属性,而是管理它们的值类型。基本上所有涉及的表都是查找表。缺点是,为了检索每个值,您必须对每个值类型进行“大小写”。
选择这些方法时,请记住这些文章(此处和此处)。这个论坛帖子也很有趣,并且与主题相关,即使它是关于本地化的。
如果您觉得需要,您也可以使用Tomalak 的答案并添加强类型。
如果您想灵活处理类别和属性,则应创建以下表格:
当您想通过 mroe 共享一个类别而不是一个产品时,您必须为 n:m 连接创建一个链接表:
您将不得不在您的查询中加入一些连接,但是使用正确的索引,您应该能够快速查询您的数据。
我最近不得不这样做,我正在使用 NHibernate,我有三个实体
产品类别选项 OptionCategory
一个产品有 1* 个类别
一个产品有 1* 选项
一个选项有 1 个选项类别
一旦设置好,您就可以使用 Nhibernate 缓存
干杯