Postgres 数据库:如何对多个属性进行建模,这些属性也可以具有多个值,并且与其他两个实体有关系

use*_*631 1 postgresql database-design relational-database

我有三个实体:项目、类别和属性。

AnItem可以是一个,也可以是多个Categories,因此存在 N:M 关系。

Item               ItemCategories                   Categories     

id name            item_id category_id             id    name     
1  alfa               1         1                   1   chipset
                      1         2                   2   interface 
Run Code Online (Sandbox Code Playgroud)

根据它们所在的“类别”,可以Item有多个。Attributes

例如,Category“芯片组”中的项目可以具有以下属性:“接口”、“内存”、“技术”。

这些属性有一组不经常更改的预定义值,但它们可以更改。

例如:“内存”只能是 ddr2、ddr3、ddr4。

Attributes                                 CategoryAttributes

id name     values                          category_id attribute_id    
1  memory   {ddr2, ddr3, ddr4}              1              1
Run Code Online (Sandbox Code Playgroud)

Item“芯片组”中的Category可以访问该属性Attribute,并且只能具有 Null 或该属性的预定义值。

我想使用EnumorJson作为属性值,但我还有另外两个条件:

项目属性

item_id attribute_id   value
 1        1          {ddr2, ddr4}
Run Code Online (Sandbox Code Playgroud)

1) 如果一个属性出现在2个类别中,并且一个Ithe同时出现在两个类别中,则该属性只能显示一次。

2)我需要使用 的值rank,因此如果一个项目出现两个对应的属性值,如果只有一个,则排名应该更大,否则该值不存在。

3)为属性创建单独的表不是一个选项,因为数量不固定,并且可能很大。

因此,我不知道数据库设计中的最佳选择是限制值并用于订单排名。

Car*_*les 5

您描述的问题是典型的开放模式或垂直数据库,这是某种EAV模型的经典用例。

EAV 是一个复杂而强大的范例,它允许潜在的开放模式,同时尊重数据库范式,并允许拥有您所需要的:根据同一实体的特定实例具有可变数量的属性。

这就是使用关系数据库的电子商务中通常发生的情况,因为不同的产品具有不同的属性(即口红有颜色,但对于硬盘驱动器来说,您可能不关心颜色而是关心容量),并且拥有一个没有意义属性表,因为数量不固定并且可能很大,并且对于大多数行来说,会有很多NULL值(这是稀疏矩阵的数学概念,在数据库表中看起来非常难看)

您可以查看Magento DB Model,这是大规模纯 EAV 的真正参考,或者Wikipedia,但您可能可以稍后再做,而现在,您只需要基础知识:

基本思想是将属性及其相应的值作为行而不是列存储在单个表中。

在更简单的实现中,表至少有三列:(entity通常是实体的外键,或实体类型/类别),attribute(这可以是字符串,或者在更复杂的系统中的外键)和value。

在我之前的示例中,过于简单化,我们可以有一个像这样的表,其中列出了属性名称及其值

Item table                 Attributes table

+------+--------------+   +-------------+-----------+-------------+
|   id | name         |   |     item_id | attribute | value       |
+------+--------------+   +-------------+-----------+-------------+
|    1 | "hard drive" |   |           2 |   "color" |       "red" | 
+------+--------------+   +-------------+-----------+-------------+
|    2 | "lipstick"   |   |           2 |   "price" |          10 | 
+------+--------------+   +-------------+-----------+-------------+
                          |           1 | "capacity"|       "1TB" | 
                          +-------------+-----------+-------------+
                          |           1 |   "price" |         200 | 
                          +-------------+-----------+-------------+

 So for every item, you can have a list of attributes. 
Run Code Online (Sandbox Code Playgroud)

由于您的模型更复杂,有更多的约束,所以我们需要调整这个模型。

  • 由于您想要限制可能的值,因此您将需要一个值表
  • 由于您将有一个值表,因此值必须引用一个属性,因此您需要属性有一个 id,因此您将有一个属性表
  • 为了明确和严格哪些类别具有哪些属性,您需要一个类别属性表

有了这个,你最终会得到类似的东西

类别表 类别 ID 和名称列表

+------+--------------+ 
|   id | name         |  
+------+--------------+ 
|    1 | "chipset"    | 
+------+--------------+ 
|    2 | "interface"  | 
+------+--------------+ 
Run Code Online (Sandbox Code Playgroud)

属性表 属性 ID 及其名称的列表

+------+--------------+ 
|   id | name         |  
+------+--------------+ 
|    1 | "interface"  | 
+------+--------------+ 
|    2 | "memory"     | 
+------+--------------+ 
|    3 | "tech"       | 
+------+--------------+
|    4 | "price"      | 
+------+--------------+
Run Code Online (Sandbox Code Playgroud)

类别-属性表 什么类别有什么属性。请注意,一个属性(即 4)可以属于 2 个类别

+--------------+--------------+ 
| attribute_id | category_id  |  
+--------------+--------------+ 
|            1 |            1 | 
+--------------+--------------+ 
|            2 |            1 | 
+--------------+--------------+ 
|            3 |            1 | 
+--------------+--------------+ 
|            4 |            1 | 
+--------------+--------------+ 
|            4 |            2 | 
+--------------+--------------+ 
Run Code Online (Sandbox Code Playgroud)

值表 每个属性的可能值列表

+----------+--------------+--------+
| value_id | attribute_id | value  |
+-------------+-----------+--------+
|        1 |            2 | "ddr2" | 
+----------+--------------+--------+
|        2 |            2 | "ddr3" | 
+----------+--------------+--------+
|        3 |            2 | "ddr4" | 
+----------+--------------+--------+
|        4 |            3 |"tech_1"| 
+----------+--------------+--------+
|        5 |            3 |"tech_2"| 
+----------+--------------+--------+
|        6 |     ...      |   ...  | 
+----------+--------------+--------+
|        7 |     ...      |   ...  | 
Run Code Online (Sandbox Code Playgroud)

最后,你可以想象,

项目属性表将每行列出一个属性值

+----------+--------------+-------+
| item_id  | attribute_id | value |
+----------+-----------+----------+
|        1 |            2 |     1 | 
+----------+--------------+-------+
|        1 |            2 |     3 | 
+----------+--------------+-------+

Meaning that item 1, for attribute 2 (`memory`), has values 1 and 3 (`ddr2` and `ddr3`)
Run Code Online (Sandbox Code Playgroud)

这将涵盖您的所有条件:

  • 属性数量无限制,需要多大就多大,不固定
  • 可以明确定义什么类别有什么属性
  • 两个类别可以具有相同的属性
  • 如果 1 件商品属于具有相同属性的两个类别,则只能显示一个(即将SELECT * from Category-Attribute where category_id in (SELECT category_id from ItemCategories where item_id = ...)为您提供符合条件的属性列表,即使 2 个类别具有相同属性,也只能显示其中一个)
  • 你可以做一个rank,我想我没有足够的信息来进行这个查询,但是这是一个完全标准化的模型,当然,你可以做一个排名。您在这里几乎拥有完整的模型,因此您肯定可以找出查询。

这与 Magento 使用的模型非常相似。它非常强大,但当然,它可能会变得难以管理,但如果我们想要保持模型严格并确保它将强制执行约束并接受所有 SQL 函数,那么这是最好的方法。NoSQL对于不太严格的系统,选择具有更灵活模式的数据库始终是一个选择。