Postgres 数据库:如何对可以具有多个值并与其他两个实体有关系的多个属性建模

Postgres database: how to model multiple attributes that can have also multiple value, and have relations to other two entities

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

一个Item可以是一个或多个Categories,所以有N:M关系

Item               ItemCategories                   Categories     

id name            item_id category_id             id    name     
1  alfa               1         1                   1   chipset
                      1         2                   2   interface 

一个 Item 可以有多个 Attributes,具体取决于它们所在的 'Categories'。

例如,Category 'chipset' 中的项目可以具有以下属性:'interface'、'memory' 'tech'.

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

例如:'memory'只能是ddr2,ddr3,ddr4.

Attributes                                 CategoryAttributes

id name     values                          category_id attribute_id    
1  memory   {ddr2, ddr3, ddr4}              1              1

'chipset' Category 中的 Item 可以访问 Attribute 并且只能具有 Null 或属性的预定义值。

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

项目属性

item_id attribute_id   value
 1        1          {ddr2, ddr4}

1) 如果一个Attribute出现在2个Categories中,一个Ithe同时出现在两个Categories中,一个属性只能显示一次。

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

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

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

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

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

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

您可以看一下 Magento DB Model, a true reference in pure EAV at scale, or Wikipedia,但您可能稍后再做,现在,您只需要基础知识:

基本思想是在单个 table.

中将属性及其对应值存储为行而不是列

在更简单的实现中,table 至少有三列:entity(通常是实体的外键,或实体 type/category),attribute(这可以是一个字符串,o 在更复杂的系统中是一个外键)和 value

在我之前的示例中,过于简单化了,我们可以有一个像这样的 table,它列出了

的属性名称及其值
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. 

由于你的模型比较复杂,约束也比较多,所以我们需要适配这个模型。

  • 由于您想限制可能的值,因此您需要 table 值
  • 因为你会有一个值table,这些值必须引用一个属性,所以你需要属性有一个id,所以你会有一个属性table
  • 为了明确和严格什么类别有什么属性,你需要一个类别属性table

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

的东西

类别table 类别 ID 和名称列表

+------+--------------+ 
|   id | name         |  
+------+--------------+ 
|    1 | "chipset"    | 
+------+--------------+ 
|    2 | "interface"  | 
+------+--------------+ 

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

+------+--------------+ 
|   id | name         |  
+------+--------------+ 
|    1 | "interface"  | 
+------+--------------+ 
|    2 | "memory"     | 
+------+--------------+ 
|    3 | "tech"       | 
+------+--------------+
|    4 | "price"      | 
+------+--------------+

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

+--------------+--------------+ 
| attribute_id | category_id  |  
+--------------+--------------+ 
|            1 |            1 | 
+--------------+--------------+ 
|            2 |            1 | 
+--------------+--------------+ 
|            3 |            1 | 
+--------------+--------------+ 
|            4 |            1 | 
+--------------+--------------+ 
|            4 |            2 | 
+--------------+--------------+ 

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

+----------+--------------+--------+
| value_id | attribute_id | value  |
+-------------+-----------+--------+
|        1 |            2 | "ddr2" | 
+----------+--------------+--------+
|        2 |            2 | "ddr3" | 
+----------+--------------+--------+
|        3 |            2 | "ddr4" | 
+----------+--------------+--------+
|        4 |            3 |"tech_1"| 
+----------+--------------+--------+
|        5 |            3 |"tech_2"| 
+----------+--------------+--------+
|        6 |     ...      |   ...  | 
+----------+--------------+--------+
|        7 |     ...      |   ...  | 

最后,如你所想,

Item-Attribute table 将每行列出一个属性值

+----------+--------------+-------+
| 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`)

这将涵盖您的所有条件:

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

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