为多对多关系(产品的变体)的组合设计SQL模式

时间:2013-10-02 18:33:07

标签: sql schema e-commerce database-schema database-administration

我希望这个标题有点帮助。我正在使用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美元的蓝色产品。使用上面的数据库结构,如果不解析文本就不可能做到这一点,这是我想要避免的。

感谢任何帮助/建议=)

6 个答案:

答案 0 :(得分:33)

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

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
                            );




create table product_details(product_detail_id int auto_increment primary key,
                             product_Variants_id int,

                             value_id int
                             );

insert into product_Variants(product_id,productVariantName,sku,price)
values (1,'red-wool' ,'a121',50);

insert into product_details(product_Variants_id , value_id)
values( 1,1),(1,4);

insert into product_Variants(product_id,productVariantName,sku,price)
values (1,'red-polyester' ,'a122',50);

insert into product_details(product_Variants_id , value_id)
values( 2,1),(2,5);

答案 1 :(得分:16)

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

当您销售时," XYZ套衫,尺码M,蓝色型号",后者对应SKU。它作为XYZ套衫(产品)销售,它具有一组属性(大小和颜色),每个属性都有自己的一组潜在值。并非后者的所有可能组合都可能产生有效的可交付成果:你不会找到荒谬的薄牛仔裤。 SKU,产品,属性,属性值。

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

我希望上述内容可以解决您的困惑以及您的问题和问题源于何处。

就架构而言,您需要以下内容:


产品

  • #product_id
  • 名称
  • 描述

或者,也可以添加:

  • IN_STOCK

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

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

in_stock是一个有希望的自我解释的旗帜,理想情况下由触发器维持。如果与该产品相关的任何 SKU都有库存,则应该是真的。


product_attributes

  • PRODUCT_ID
  • #attribute_id
  • 名称

product_attribute_values

  • attribute_id
  • #value_id

这只包含颜色,大小等等,以及蓝色,红色,S,M,L等值。

请注意product_id字段:为每个产品创建一组新的属性和值。尺寸根据产品而变化。有时它是S,M,L等;其他时候,它将是38,40,42,什么不是。有时候,尺寸就足够了;其他时候,你需要宽度和长度。蓝色可能是该产品的有效颜色;另一个可能会提供海军,皇家蓝,蓝绿色等等。不要认为某个产品的属性与另一个产品的属性之间存在任何关系;它们存在时的相似之处完全是装饰性的和巧合的。


的SKU

  • PRODUCT_ID
  • #sku_id

(可选)添加:

  • 名称
  • 条形码
  • 股票

这相当于发货的可交付成果。

它实际上是下面最重要的表格。 ,而不是product_id,几乎可以肯定应该在客户订单中引用。它也应该被引用到库存等等。 (我在后两点看到的唯一例外是当你卖出一些非常通用的东西。但即便如此,根据我的经验来处理这个问题的更好方法是在可互换的SKU之间投入一个nm关系。)

如果添加名称字段,则主要是为了方便起见。如果保留为null,请使用应用程序端代码使其与通用产品的名称相对应,必要时使用相关的属性名称和值进行扩展。填充它允许用更自然的东西(" Levis' 501)重新描述后一个通用名称(" Levis' 501,W:32,L:32,颜色:深蓝") ,32x32,深蓝色")。

如果重要的话,从长远来看,使用触发器可以更好地维护库存,并在后台使用复式簿记架构。这样就可以在您遇到的大量真实场景中区分现货和今天可用的货物(这是您实际需要的数字)与库存但已售出的数量。哦,而且......如果您需要出售以千克或升为单位的任何东西,它偶尔会是数字而不是整数。如果是这样,请务必添加额外的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的东西在必要时也必须考虑在内到库存。例如,用于笔记本电脑的更大HD选项实际上是由于(非常有效的)UI考虑而伪装成选项的相同产品(正常与大HD尺寸)的变体。相比之下,将笔记本电脑作为圣诞礼物包装是一个真正的选择,它在记账条款中引用了完全独立的SKU(例如.8m的礼品包装) - 并且,如果您需要提出平均边际成本,那么工作人员的时间。

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

答案 2 :(得分:5)

我会使用4个表:

generic_product: product_id, name, description 

e.g。 1,'地毯','咖啡地毯'/ 2,'马克杯','咖啡杯'

generic_product_property: product_id, property_id, property_name 

e.g。 1,10,'颜色'/ 1,11''材料'

sellable_product: sku, product_id, price 

e.g。 'A121',1,50.00 /'A122',1,45.00

sellable_product_property: sku, property_id, property_value 

e.g。 'A121',10,'red'/'A121',11,'wool'/'A122',10,'green'/'A122',11,'wool'

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

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

答案 3 :(得分:1)

这类似于我在SO

上看到的另一个问题

Designing a database : Which is the better approach?

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

如果你有这么多不同的变种,你也可能想要查看键值数据库或其他一些NoSQL解决方案。

答案 4 :(得分:1)

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

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

答案 5 :(得分:0)

Sku是您的主要钥匙。您可以使用sku设置变量表的外键关系。完全忘掉productid。

创建表x(sku,价格,描述)主键sku