25

我希望标题有点帮助。我使用 MySQL 作为我的数据库

我正在建立一个产品数据库,但不确定如何处理存储产品变体的价格/SKU。一个产品可能有无限的变体,每个变体组合都有自己的价格/SKU/等。

这就是我目前设置产品/变体表的方式:

PRODUCTS
+--------------------------+
| id | name | description  |
+----+------+--------------+
| 1  | rug  | a cool rug   |
| 2  | cup  | a coffee cup |
+----+------+--------------+

PRODUCT_VARIANTS
+----+------------+----------+-----------+
| id | product_id | variant  | value     |
+----+------------+----------+-----------+
| 1  | 1          | color    | red       |
| 2  | 1          | color    | blue      |
| 3  | 1          | color    | green     |
| 4  | 1          | material | wool      |
| 5  | 1          | material | polyester |
| 6  | 2          | size     | small     |
| 7  | 2          | size     | medium    |
| 8  | 2          | size     | large     |
+----+------------+----------+-----------+

(`products.id` is a foreign key of `product_variants.product_id`)

我用这个示例数据创建了一个 SQLFiddle:http ://sqlfiddle.com/#!2/2264d/1

用户可以输入任何变体名称 ( product_variants.variant) 并可以为其分配任何值 ( product_variants.value)。用户可以输入的变体/值的数量不应受到限制。

这就是我的问题出现的地方:每次有人添加具有以前不存在的变体的产品时,为每个变体存储价格/SKU,而不添加新的表/列。

每个变体可能具有相同的价格,但每个产品的 SKU 是唯一的。例如,产品1有 6 种不同的组合(3 种颜色 * 2 种材料),而产品2只有 3 种不同的组合(3 种尺寸 * 1)。

我考虑过将组合存储为文本,即:

+------------+-----------------+-------+------+
| product_id | combination     | price | SKU  |
+------------+-----------------+-------+------+
| 1          | red-wool        | 50.00 | A121 |
| 1          | red-polyester   | 50.00 | A122 |
| 1          | blue-wool       | 50.00 | A123 |
| 1          | blue-polyester  | 50.00 | A124 |
| 1          | green-wool      | 50.00 | A125 |
| 1          | green-polyester | 50.00 | A125 |
| 2          | small           | 4.00  | CD12 |
| 2          | medium          | 4.00  | CD13 |
| 2          | large           | 3.50  | CD14 |
+------------+-----------------+-------+------+

但是必须有一种更好的、标准化的方式来表示这些数据。假设情况:我希望能够搜索不到 10 美元的蓝色产品。使用上述数据库结构,不解析文本是不可能的,这是我想要避免的。

任何帮助/建议表示赞赏=)

4

7 回答 7

46

对您的问题应用规范化,解决方案如给定。在SQL Fiddle上运行并查看它。

CREATE TABLE products (
    product_id  int AUTO_INCREMENT PRIMARY KEY,
    name        varchar(20),
    description varchar(30)
);

INSERT INTO products
    (name, description)
VALUES
    ('Rug', 'A cool rug' ),
    ('Cup', 'A coffee cup');

-- ========================================

CREATE TABLE variants (
    variant_id int AUTO_INCREMENT PRIMARY KEY,
    variant    varchar(50)
);

INSERT INTO variants
    (variant)
VALUES
    ('color'),
    ('material'),
    ('size');

-- ========================================

CREATE TABLE variant_value (
    value_id   int AUTO_INCREMENT PRIMARY KEY,
    variant_id int,
    value      varchar(50)
);

INSERT INTO variant_value
    (variant_id, value)
VALUES
    (1, 'red'),
    (1, 'blue'),
    (1, 'green'),
    (2, 'wool'),
    (2, 'polyester'),
    (3, 'small'),
    (3, 'medium'),
    (3, 'large');

-- ========================================

CREATE TABLE product_variants (
    product_variants_id int AUTO_INCREMENT PRIMARY KEY,
    product_id          int,
    productvariantname  varchar(50),
    sku                 varchar(50),
    price               float
);

INSERT INTO product_variants
    (product_id, productvariantname, sku, price)
VALUES
    (1, 'red-wool', 'a121', 50),
    (1, 'red-polyester', 'a122', 50);

-- ========================================

CREATE TABLE product_details (
    product_detail_id   int AUTO_INCREMENT PRIMARY KEY,
    product_variants_id int,
    value_id            int
);

INSERT INTO product_details
    (product_variants_id, value_id)
VALUES
    (1, 1),
    (1, 4),
    (2, 1),
    (2, 5);
于 2013-10-05T17:01:17.830 回答
32

您的部分问题源于产品和 SKU 之间的混淆。

当您销售“XYZ 套头衫,尺寸 M,蓝色款”时,后者对应一个 SKU。它作为 XYZ 套头衫(产品)销售,它具有一组属性(尺寸和颜色),每个属性都有自己的一组潜在价值。并非后者的所有可能组合都可能产生有效的可交付成果:您不会发现又瘦又长的牛仔裤。SKU、产品、属性、属性值。

当用户想要一件 10 美元的蓝色套头衫时,他实际上是在寻找产品类别中的 SKU。

我希望以上内容可以消除您的困惑以及您的问题和疑问的根源。

在架构方面,你想要这样的东西:


产品

  • #product_id
  • 姓名
  • 描述

(可选)还添加:

  • 价格
  • 有存货

这是一个营销相关的表格。没有其他的。如果营销之外的任何事情在您的应用程序中使用产品,那么您最终将陷入痛苦的世界。

价格(如果存在)是用于在 SKU 中为空时填充该字段的主价格。这使得价格输入更加用户友好。

in_stock 是一个希望不言自明的标志,理想情况下由触发器维护。如果与该产品相关的任何SKU 有库存,则应该是正确的。


产品属性

  • product_id
  • #attribute_id
  • 姓名

product_attribute_values

  • 属性ID
  • #value_id
  • 价值

这仅包含颜色、大小等内容,以及它们的值,如蓝色、红色、S、M、L。

注意 product_id 字段:为每个产品创建一组新的属性和值。尺寸因产品而异。有时是 S、M、L 等;其他时候,它将是 38、40、42,等等。有时,大小就足够了;其他时候,您需要宽度和长度。蓝色可能是该产品的有效颜色;另一个可能会提供海军蓝、皇家蓝、蓝绿色等等。不要假设一种产品的属性与另一种产品的属性之间存在任何关系;相似之处,如果存在的话,完全是装饰性的和巧合。


SKU

  • product_id
  • #sku_id
  • 价格

(可选)添加:

  • 姓名
  • 条码
  • 股票

这对应于交付的交付物。

它实际上是下面最重要的桌子。this,而不是 product_id,几乎可以肯定是客户订单中应该引用的内容。这也是库存等方面应该参考的内容。(我见过的后两点的唯一例外是当你销售一些真正通用的东西时。但即便如此,根据我的经验,处理这个问题的更好方法是在可互换的 SKU 之间折腾一个 nm 关系。)

如果添加名称字段,主要是为了方便。如果留空,则使用应用端代码使其与通用产品的名称相对应,并在必要时使用相关的属性名称和值进行扩展。填充它允许用更自然的东西(“Levis' 501, 32x32, Dark Blue”)改写后一个通用名称(“Levis' 501, W: 32, L: 32, Color: Dark Blue”)。

万一这很重要,从长远来看,使用触发器可以更好地维护库存,并在后台使用复式簿记模式。在您将遇到的众多现实世界场景中,这允许区分今天有库存和可发货(这是您在此处实际需要的数字)与有库存但已售出。哦,而且……如果您需要出售以公斤或升为单位的任何物品,它有时是一个数字,而不是一个整数。如果是这样,请务必添加一个额外的 is_int 标志,以避免客户向您发送 .1 笔记本电脑的订单。


product_variants

  • product_id
  • #sku_id
  • #attribute_id
  • value_id

这会将可交付成果的 id 与相应的属性和值链接起来,以便生成默认名称。

主键打开(sku_id,attribute_id)。

您可能会发现 product_id 字段异常。除非您添加外键引用:

  • SKU(product_id、sku_id)
  • product_attributes (product_id, attribute_id)
  • product_attribute_values (attribute_id, value_id)

(如果您决定添加这些外键,请不要忘记相应元组上的额外唯一索引。)


最后补充三点。

首先,我想再次强调,就流程而言,并非所有属性和值的组合都能产生有效的可交付成果。宽度可能是 28-42,长度可能是 28-42,但您可能不会看到一条非常紧身的 28x42 牛仔裤。您最好不要默认自动填充每个产品的所有可能变体:添加 UI 以根据需要启用/禁用它们,使其默认选中,以及名称、条形码和价格字段。(名称和价格通常留空;但有一天,您只需要组织一次蓝色套头衫的销售,理由是该颜色已停产,而您继续销售其他选项。)

其次,请记住,如果您需要额外管理产品选项,那么许多实际上是变相的产品属性,而那些不产生新 SKU 的产品在库存管理时也​​必须考虑在内。例如,笔记本电脑的更大高清选项实际上是同一产品的变体(普通与大高清尺寸),由于(非常有效的)UI 考虑,它伪装成一个选项。相比之下,将笔记本电脑包装为圣诞礼物是一种真正的选择,它在记账方面引用了一个完全独立的 SKU(例如 .8m 的礼品包装)——而且,如果您需要计算平均边际成本,则只需一小部分员工时间。

最后,您需要为您的属性、它们的值和后续变体提出一种排序方法。为此,最简单的方法是在属性和值表中添加一个额外的位置字段。

于 2013-10-10T17:13:56.940 回答
7

我会使用 4 张桌子:

generic_product: product_id, name, description 

例如 1, 'rug', 'a coffee rug' / 2, 'mug', 'a coffee mug'

generic_product_property: product_id, property_id, property_name 

例如 1, 10, '颜色' / 1, 11, '材质'

sellable_product: sku, product_id, price 

例如'A121', 1, 50.00 / 'A122', 1, 45.00

sellable_product_property: sku, property_id, property_value 

例如 'A121', 10, 'red' / 'A121', 11, 'wool' / 'A122', 10, 'green' / 'A122', 11, 'wool'

这将允许您的用户为他想要的可销售产品定义任何属性。

您的应用程序必须通过其业务逻辑确保完全描述了可销售产品(检查是否为每个适用的通用产品属性定义了可销售产品属性)。

于 2013-10-05T07:52:25.643 回答
1

这类似于我在 SO 上看到的另一个问题

设计数据库:哪种方法更好?

如果您在那里看一下,您会发现您基本上是在问相同的窄(基于属性)与宽表问题。我已经根据场景使用了这两种方法,但我会非常小心你现在实现它的方式。事实上,确实没有一种好的方法可以将这些变体与 SKU 匹配(至少不是我能想到的),这可能会迫使您更改表格。

如果您有这么多不同的变体,您可能还想研究键值数据库或其他一些 NoSQL 解决方案。

于 2013-10-02T21:33:49.437 回答
1

Sku 是您的主键。您可以使用 sku 设置与变体表的外键关系。完全忘记 productid 。

创建表x(sku, price, description) 主键sku

于 2013-10-11T13:46:57.293 回答
1

一般而言,您正在寻找所谓的石斑鱼或垃圾维度。基本上它只是每个组合的一行。@sahalMoidu 的模式看起来应该可以满足您的要求。

但在过于关注规范化之前,您需要知道数据库是用于存储数据(事务等)还是用于获取数据(维度、报告等)。即使它是一个事务性数据库,您也必须问自己您要通过规范化来完成什么。

于 2013-10-09T15:39:02.520 回答
0

我认为我们应该添加product_variant_ids而不是productVariantName删除product_details表。

还添加额外的表tbl_variant_group

tbl_variant_group

variant_group_id, title -> (1 color) , (2 size), (3 material)

tbl_variant

id, variant_group_id, title ->
(1, 1, red), (2, 2, small), (3, 3, polyester) (4, 1, green), (5, 3, cotton)

现在我们可以添加变体 id 的组合而不是productVariantName.

例如组合[1,2,3]为我们提供了xyzColor: red,Size: small和的产品Material: polyester

组合[4, 2, 5]为我们xyz提供Color: greenSize: small和的产品Material: cotton

现在,每当用户选择不同的组合时,我们都可以将其与我们存储的组合进行比较。

注意保存组合时,我们可以按标题asc顺序存储它。variant_group这样我们就可以按照相同的顺序进行 color->material->size比较red->polyester->small -> [1,3,2]

我们可以添加任意 n 个变体 id。

图片

于 2021-09-30T15:22:37.440 回答