【问题标题】:What's the optimal method to use for this database Modeling/Design?用于此数据库建模/设计的最佳方法是什么?
【发布时间】:2012-07-14 08:43:11
【问题描述】:

我正在编写一个用于厨房/食谱管理的程序。截至目前,该程序可以将新成分添加到数据库中。我的下一步是使用成分将新食谱添加到数据库中。

由于每个配方都有多种成分,并且可以将无限数量的成分添加到一个配方中,因此为每种选择的成分创建一个单独的列不会有效(我假设)。

所以我想出的存储成分选择的方法是:

  1. 将 recipe_ingredients 列添加到数据库中的配方表中,并将每种成分的名称存储在 1 个单个(文本)字段中,以逗号分隔(CSV 样式)。

  2. 编写一个 PHP 脚本,将 recipe_ingredients 列表存储到一个数组中。

  3. 对所有其他属性(recipe_ingredients_serving_size、recipe_ingredients_calories 等)执行相同操作

所以问题是:这种方法是最优的吗?如果不是,有什么更好的方法来解决这个问题?

【问题讨论】:

标签: php mysql database-design


【解决方案1】:

不是解决这个问题的理想方法。

一列中的逗号分隔值使这些值毫无用处 - 如果您只是将一堆成分连接到一个字符串中,那么按成分查询食谱会很痛苦。

另外,如果您将单个成分的卡路里计数与一种成​​分存储为逗号分隔的字符串,您将如何将它们关联起来?

理想的解决方案是为成分创建一个新表,并将名称和相关信息存储在该表中。例如

ingredients
-----------
id 
name
num_calories

有很多食谱有很多成分,所以创建一个将成分链接到特定食谱的表,这使得成分与食谱的关系是多对多的,而不是直接的多对多。

recipe_ingredients ------------------ recipe_id 成分ID

您应该在此处阅读normalization

【讨论】:

  • 感谢您的快速响应!我已经有一个单独的成分表。但我很困惑如何在创建时将所选成分链接到食谱,而不必为每个recipe_ingredient创建一个新列。不知道这是否有意义,大声笑
  • 看起来这些表也需要数量。对于成分表,您需要 1) 具有该卡路里数的成分的数量,以及 2) 表示该数量的单位。对于 recipe_ingredients 表,您需要配方中的数量,以与成分表中相同的单位表示。
  • recipe_ingredients 每个配方中的每种成分都有一行。例如,如果 ID 为 6 的食谱有 10 种成分,那么 recipe_ingredients 将有 10 行,recipe_id 为 6,每种成分一个。这可能听起来很麻烦,但它是一种非常灵活的信息存储方式,并且 mysql 可以处理具有数百万行的表而不会降低太多速度。
  • @octern 但这不会要求每个食谱都有一个单独的表吗?而不是每个食谱单独一行?我很困惑,大声笑。
  • 不,只有一张桌子。当您想显示配方 #6 时,您可以查询 recipe_ingredients 以获取 recipe_id=6 的每一行。您返回 10 行,每种成分一个,然后将它们放在您显示的食谱中。在不同的页面上,如果您想显示配方 8,您将查询同一个表,但获取所有 recipe_id = 8 的行。
【解决方案2】:

这是一个简单的 N:M(多对多)关系,您制定的方法可能会在效率和管理方面造成灾难。


这是您的情况:

  • 您有两个实体:recipesingredients
  • 一种成分可能是许多食谱的一部分。
  • 一个食谱可能由许多成分组成。

只要您在任何两个实体之间建立这种关系,您就不会想要两个,而是三个表:

+-----------+     +-------------------------+     +-------------------+
| recipes   |     | recipes_has_ingredients |     | ingredients       |
+-----------+     +-------------------------+     +-------------------+
| recipe_id |     | recipe_id               |     | ingredient_id     |
| name      |     | ingredient_id           |     | name              |
| ...       |     +-------------------------+     | calories          |
+-----------+                                     +-------------------+

recipesingredients 是所谓的基表,它们存储有关该特定实体的内在信息。

recipes_has_ingredients 表是所谓的交叉引用表(或“XREF”),它存储关联这两个实体。此表中的字段:recipe_idingredient_id 都链接到它们各自的基表,并且 XREF 表中每一行中两者的组合是唯一的。它基本上将每个recipe_id 可能具有的许多关联映射到不同的ingredient_id,反之亦然。

为什么这种设计会促进多对多关系?因为允许该 XREF 表中的数据如下所示:

+-----------------------------+
| recipe_id  |  ingredient_id |
+-----------------------------+
| 1          |  1             |
| 1          |  2             |
| 1          |  3             |
| 2          |  1             |
| 2          |  2             |
| 2          |  3             |
| 3          |  1             |
| 3          |  2             |
| 3          |  3             |
+-----------------------------+

您可以清楚地看到:一种配方与许多 (3) 种成分相关联,一种成分与许多 (3) 种配方相关联。还要注意任何一列中的值是如何允许重复的,但是两列的组合是独一无二的——这确实是这个设计的关键方面N:M 关系工作。

以下是一些简单示例,说明如何使用此设计轻松检索和管理数据:

// Given a particular recipe_id, retrieve all ingredients used in that recipe:   

SELECT     name
FROM       recipes_has_ingredients
INNER JOIN ingredients USING (ingredient_id)
WHERE      recipe_id = <id>

// Retrieve the name of recipe (of id 4), and total amount of calories it has:

SELECT      a.name, 
            SUM(c.calories) AS calorie_count
FROM        recipes a
INNER JOIN  recipes_has_ingredients b ON a.recipe_id = b.recipe_id
INNER JOIN  ingredients c ON b.ingredient_id = c.ingredient_id
WHERE       a.recipe_id = 4
GROUP BY    a.recipe_id, 
            a.name

// Given a list of ingredient_id's, retrieve all recipes that contain 
// ALL of the listed ingredients

SELECT     name
FROM       recipes
INNER JOIN recipes_has_ingredients USING (recipe_id)
WHERE      ingredient_id IN (1,2,3)
GROUP BY   recipe_id
HAVING     COUNT(*) = 3

// Given a particular recipe_id (id 6), add two more ingredients 
// that it has (ids 4 & 9):

INSERT INTO recipes_has_ingredients VALUES (6,4), (6,9);

// Delete a particular recipe:

DELETE FROM recipe WHERE recipe_id = 4

^ 如果您正确定义了关系之间的 CASCADE 规则,上述 DELETE 操作也会删除该配方的所有关联。


回顾您的原始设计,如果您想更新或删除配方中的某些成分,或者更改成分的名称怎么办?您将需要 hacky 程序代码来修改 csv 字符串中的正确位置,或者您需要更新表中的每一行以反映单个成分中最轻微的变化。

您还可以回答许多使用原始设计无法回答的更引人注目的问题,例如:

  • 热量最高/最低的食谱?
  • 大多数食谱都包含哪些成分?

...不胜枚举,实施此设计的好处将为您提供良好的服务。通过正确的方式做事,您将避免自己遭受很多困难和痛苦。 =)

【讨论】:

  • EEEEEXCELLENT 信息,赞恩!谢谢!我得把它打印出来研究一下:)
  • ^^^非常感谢!是的,我在这个(编程)方面足够新,仍然会犯可怕的新手错误......但在这一点上只有足够的经验来在我犯错误之前意识到错误(嗯......真的很糟糕,反正哈哈) .我认为这不是犹太洁食,所以我想我会在这里发帖并获得一些指导。再次感谢!
猜你喜欢
  • 2011-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-08
  • 2018-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多