数据库设计:产品和产品组合

6
我正在销售一些产品。有时,该产品实际上是其他产品的组合。例如,我正在销售:
- 热狗 - 苏打水 - 热狗+苏打水套餐
我应该如何对这种情况进行建模?我是否应该有一个产品表来列出各个产品,然后再有一个描述组合的product_combo表,以及与产品和product_combo相关联的另一个表来详细列出组合中的产品?这对我来说似乎很简单。
但是,如果我想在一个表中记录所有销售情况呢?也就是说,我不想有product_sales表和product_combo_sales表。我希望所有销售都在一个表中。我有点不确定如何以便于以后在一个表中记录所有销售的方式来建模产品和产品组合。
有什么建议吗?
注意:我想知道是否可以使用父子关系将产品和产品组合放在一个表中。有了一个表,那么记录销售就不难了。我只需要实现一个业务规则,即当已经针对该组合记录了销售时,编辑产品组合时,该编辑实际上会导致一个新条目的产生。虽然可能会变得混乱。
3个回答

4
这取决于您的系统实际需要做什么。需要跟踪库存的系统需要了解“套餐”需要扣减库存一个热狗和32盎司的苏打水(或其他物品)。然而,只需跟踪订单和销售额的系统并不关心“套餐”的内容 - 只需确认已售出并得到报酬就可以。
话虽如此,让我们假设您需要库存系统。通过稍微更改定义,您可以减少复杂性。考虑以(1)库存项目和(2)菜单项目为基础。你的inventory_items表包含你购买和跟踪为库存的物品(热狗,苏打水等)。menu_items表包含你销售的物品(Big Dog Combo Meal,Hot Dog(仅三明治)等)。
您可以有一些菜单项恰巧与库存项同名,但对于这些菜单项,请像处理组合项一样将它们视为同一记录,并将其放入链接表中。
 inventory_items            menu_items       recipes (menu_item, inventory, qty)
 ---------------            ------------     ----------
 hot dog                    Hot Dog          Hot Dog, hot dog, 1
 hot dog bun                Hamburger        Hot Dog, hot dog bun, 1
 hamburger patty (4oz)      Big Dog Combo    Hamburger, hamburger patty (4oz), 1
 hamburger bun              Soda (32oz)      Hamburger, hamburger bun, 1
 cola                                        Big Dog Combo, hot dog, 1
 ginger ale                                  Big Dog Combo, hot dog bun, 1
                                             Big Dog Combo, *soda, 32
                                             Soda (32oz), *soda, 32

构建这个例子时,发现即使是不起眼的热狗也有两个组成部分(必须计算面包),而不仅仅是一个。为了得出最简单的情况(拥有单个组成部分的菜单项),我将苏打加到了菜单中。但需要考虑的是,如果您要盘点非食品物品(杯子),即使是简单的苏打也将有两个组成部分(如果您正在盘点吸管,则有三个)。
请注意,使用此设计不会有处理套餐和非套餐项的特殊代码路径。所有与菜单相关的功能都将仅使用“menu_items”表,所有与库存和食品准备相关的功能都将连接“menu_items”到“recipes”(如果需要其他字段,则连接“inventory_items”)。
您需要为可选组成部分(酸菜、芥末酱、辣椒等)和可以从不同库存项目中选择的组成部分(在此模型中表示为*苏打)进行特殊处理,但这应该能让您开始。

嗨Larry,我还在思考你的解决方案。我试图解决的问题实际上不仅仅是关于食物的。它也可能涉及其他物品。我正在分析你的解决方案,看它是否足够通用以满足我的需求。 - StackOverflowNewbie
当我进行销售时,我记录什么?我存储到 menu_items 的 PK 的引用吗?如果是这样,那么意味着一旦对该特定项目的销售完成,我需要保留该记录以供历史目的。这样我就会知道 menu_item 的 "Hot Dog" 的销售实际上是指热狗和热狗面包在销售时。如果稍后编辑该 menu_item(例如,将其更改为热狗、热狗面包、芥末),则该编辑实际上应导致在 menu_item 表中创建一个新条目。 - StackOverflowNewbie
或者,我可以通过复制“食谱”中相关的记录,将菜单项“热狗”的含义存储在销售记录中。如果以后“热狗”的含义发生变化,那也没关系。它的含义已经存储在销售记录中了。我想,一旦销售了某个菜品,就不应该允许硬删除该菜品(为了保留历史数据);我只会为其设置一个标志。你有什么想法? - StackOverflowNewbie

1

你们两种方法都可以。但是至少还有另一种解决问题的方式,即对产品组合应用折扣(这意味着你也可以有选择地分摊折扣),例如:

CREATE TABLE products
(
  id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(128),
  description TEXT,
  price INT
);
CREATE TABLE combo_discounts
(
  id NOT NULL PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(128),
  description TEXT
);
CREATE TABLE cd_products
(
  cd_id INT /* REFERENCES combo_discounts.id */,
  p_id INT /* REFERENCES product.id */
  price_reduction INT
);
CREATE TABLE sales
(
  id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  location ...whatever...
);
CREATE TABLE sales_items
(
  sale_id INT /* REFERENCES sales.id */
  p_id INT /* REFERENCES product.id */
  cd_discount INT /* REFERENCES cd_products.cd_id */
);

请记住,您需要使用过程式代码来分配折扣销售(并在进行时标记每个已售出的物品),以解决某人购买2个热狗和一杯苏打水(因此只获得一个折扣)的问题。

...因此,销售的总价为

SELECT SUM(p.price)-SUM(cd.price_reduction)
FROM sales s INNER JOIN sales_items si ON (si.sale_id=s.id)
   LEFT JOIN cd_products cdp ON (si.cd_discount = cdp.cd_id
      AND si.p_id=cdp.p_id)
AND s.id=?

-1

我建议您从“订单”和“商品”方面考虑。一个订单包含多个商品。商品可以是不同的“产品”。因此,“订单”的示例可以是:

1)热狗

2)苏打水

3)热狗+苏打水

“商品”的示例可以是:

A)热狗

B)苏打水

这样,您可以在订单表中保留销售记录。 我认为您不需要在数据库中列出“套餐”的价格。 这是应该在代码中应用的业务逻辑,而不是在数据库中。 您可以稍后在代码中应用所有折扣。


组合价格通常比单独购买商品的总和更低。 - StackOverflowNewbie
1
是的 - 在代码中定义组合列表,如果订单包含组合列表,则对该项应用定义的折扣。这个逻辑不放在数据库中!想象一下,今天你给了5%的折扣,明天你想改成3%...或者你想添加/删除“组合”... - hovanessyan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接