【问题标题】:What is best practice for this problem (different properties for different categories)?这个问题的最佳实践是什么(不同类别的不同属性)?
【发布时间】:2010-09-18 07:21:26
【问题描述】:

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

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

例如,

  • 类别汽车具有属性颜色, 权力,...
  • 类别宠物有属性体重、年龄、...

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

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

必须为此场景设计数据库。 (SQL Server 2005)

【问题讨论】:

    标签: sql sql-server database database-design entity-attribute-value


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      【讨论】:

      • 当然,在大多数情况下,EAV 设计是矫枉过正。但是,当您确实需要更多复杂性时,它可以节省时间。
      • 当然,这个问题的所有解决方案都会破坏规范化
      • 不,每个类别有一个单独的表不会影响规范化。
      【解决方案3】:

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

      产品类别选项 OptionCategory

      一个产品有 1* 个类别

      一个产品有 1* 个选项

      一个选项有 1 个选项类别

      设置完成后,您可以使用 Nhibernate 缓存

      干杯

      【讨论】:

        【解决方案4】:

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

        1。为 Products 定义一个基表

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

        2。定义表类别

        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) - 用于整数 PropertyValueString(ProductID, CPID, PropertyID, Value) - 用于字符串 PropertyValueMoney(ProductID, CPID, PropertyID, Value) - 为了钱

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

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

        在选择这些方法时,请记住这些文章(here 和 here)。 This forum post 也很有趣,并且在某种程度上与主题相关,即使它是关于本地化的。

        您也可以使用 Tomalak's answer 并在需要时添加强类型。

        【讨论】:

        • 更正:如果您添加大量新产品类别,选项 A 将是维护的噩梦。如果您添加属于现有类别的新产品,则没有问题。无论如何,“维护噩梦”这句话夸大了事实。
        • 我一定漏掉了一个字。哦,好吧..当我写下答案时,问题已经结束了。
        【解决方案5】:

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

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

        tProduct 
          productID
          <other product details>
        
        tCategory
          categoryID
          <other category details>
        
        tProperty
          propertyID
          <other property details>
        
        tProductXCategory
          productyID
          categoryID
        
        tCategoryXProperty
          categoryID
          propertyID
        

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

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

        【讨论】:

        • 为什么需要 tProductXCategory 和 tCategoryXProperty?
        • 在 tProductXCategory 中,propertyID 应该是 productID。
        • 谢谢Ates...我不是最好的打字员。 @XSL,X 表是关系表。这就是您将类别与属性或产品与类别相关联的方式。
        • @Ates:我指的是帖子的先前版本,其中两个表都包含相同的列。
        【解决方案6】:

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

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

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

        【讨论】:

        • 哎呀,我不得不不同意这一点。每当您需要汇总产品数据(包括类别分配信息)时,查询将需要连接所有 10-15 个表以及所有其他支持表。鉴于此,可以使用单独的报告模式来最小化这个问题,但 Tomalak 的答案更具可扩展性,并且可以轻松支持报告聚合。
        • OP 声明“这个应用程序的主要要求是非常好的搜索。”如果您针对后端报告进行优化,那不是忽略了这个要求吗?
        【解决方案7】:

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

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

        当您想在多个产品上共享一个类别时,您必须为 n:m 连接创建一个链接表:

        • productCategoryPointer:ProdCatID、ProductID、CategoryID。

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

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            猜你喜欢
            • 2010-10-30
            • 2021-05-28
            • 2010-09-16
            • 2012-06-30
            • 1970-01-01
            • 1970-01-01
            • 2020-11-26
            • 2016-06-19
            • 2014-10-26
            相关资源
            最近更新 更多