在 MySQL 数据库中存储组合值 - 位掩码、外键、联结表或什么?

Storing Combinatorial Values in a MySQL database - Bit masking, foreign keys, junction tables, or what?

我正在创建一个 MySQL 数据库,其中 table 包含不同类型产品的信息。

举个例子,假设表 1 包含自行车,表 2 包含 T 恤。

我希望能够存储关于每个 table 中每个项目的颜色等信息。

例如,表 1 中可能有一辆自行车是蓝色和黄色的,表 2 中可能有一件 T 恤是红色、绿色和橙色的。

最初我打算在每个 table 中将颜色信息存储为二进制数,并使用位掩码来计算特定对象的颜色(即 1 = 红色,2 = 蓝色,4 = 绿色, 8 = 橙色 - 如果值为 5,则对象为红色和绿色)。我打算有一个外键 table,其中包含所有单一颜色的值(即红色 = 1,绿色 = 4),并使用来自 table 的值的总和作为位掩码。

我原以为这样做会 "faster",但在做出决定之前我已经 "Googling" 这个主题好几个星期了,发现 "faster" 有一个外键 table 所以可以使用索引。 (即,如果您想查看颜色值设置为 13 的 T 恤是否包含红色和绿色,而不是执行 "13 & 5" ,您将检查外键中的第 13 行 table查看红色和绿色的值是否设置为 1。)

事实是,我使用的颜色列表目前为 26 种,我预计它会增加。 (我试图不超过 31 种颜色,所以我可以使用 INT 列来存储值,其中 0 = "none"。)如果我要创建一个外键 table 来涵盖所有可能的组合在 31 种颜色中,它必须有 2,147,483,647 行和 32 列(每种可能的颜色对应一个 true/false 列)。每次添加另一种颜色时,我都必须将 table 中的行数加倍(例如,一种额外的颜色需要多 2147483648 行)。

我想像这样制作 "junction table" 会更可取:

+----------+------------+
| shirt_id |  color_id  |
+----------+------------+
|        1 | 1 (Red)    |
|        1 | 4 (Green)  |
|        1 | 8 (Orange) |
|        2 | 2 (Blue)   |
|        2 | 4 (Green)  |
+----------+------------+

那么就不需要一个巨大的 table 来列出所有可能的组合(其中绝大多数可能永远不会被使用)。问题是,每种产品类型都必须有联结 table,并且会有大量的产品类型,这意味着大量的联结 tables.

我以颜色为例,但我实际上也计划对其他几个 "stackable" 值执行此操作(例如,一个对象可以由硬木和铝以及玻璃和刨花板和 ABS 塑料和 PVC 和纸板......等等,同时进行。

我的问题是,处理这种情况最有效的方法是什么?有没有我没有想到的方法比这些方法更受欢迎?

我仅以颜色为例 - 数据库实际上有许多 "stackable" 属性(例如 material、纤维类型、纹理、饰面等)可以应用于不止一种产品类型,并且 "products" 本身将是 "generic" 并且有一个 "stackable" 值指示构成它们的组件类型(例如,"product",其中包括打包在一起的自行车和 T 恤)。

写完这篇文章后,我想使用多个联结 tables 将是最有效的方法。但是作为一个 "old-school programmer",我很难理解这样一个想法,即仅针对产品 component/color 组合 component/color 制作 [例如] 30 个不同的连接点 table 可能 "preferable" 直接分析二进制值中的位。 (我知道 MySQL 不是任天堂娱乐系统...)

性能问题取决于所使用的查询,以及数据的结构。您的问题不包含有关查询的信息。

但是,似乎没有理由不使用联结 table。这将涉及一个名为 Colors 的 table 和一个自动递增的主 ColorId。然后对于每个需要颜色的 table,您将有一个 table,例如 BikeColors 每辆自行车和颜色一行。

除非您有充分的理由,否则我不会尝试使用位操作来执行此操作。也就是说,除非您已经尝试过结点 table,并且出于某种原因不能满足您的需求。联结 table 可以利用索引。位摆弄一般不会。

另外,我想问一下为什么你有单独的 table 用于自行车和 T 恤,除非你有很多不同的列。对于大多数零售用途,一个 table 足以用于多种产品。

我曾经在不同域的字段上实施过位掩码。然而,这显然是一个将提供巨大性能改进的案例,因为它可以避免必须加入 8~10 tables。位掩码非常快,尤其是在字段被索引的情况下。
使用 32 位字段的索引,它将最多进行 31 次比较以找到结果行。
如果没有索引,它仍然必须对每一行执行位比较。

不过有个大'if'。维护起来并不容易,衬衫的颜色将始终限于位长,如果您描述的情况,我真的会选择连接点 table 并确保在您的外键上有索引。