2

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

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

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

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

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

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

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

4

2 回答 2

2

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

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

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

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

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

有许多包含许多成分的食谱,因此创建一个将成分链接到特定食谱的表,这使得成分与食谱的关系是多对多的,而不是直接的多对多。

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

你应该在这里阅读规范化

于 2012-07-15T00:35:19.490 回答
2

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


这是你的情况:

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

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

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

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

recipes_has_ingredients表就是所谓的交叉引用表(或“XREF”),它存储两个实体之间的关联。此表中的字段:recipe_id并且ingredient_id都链接到它们各自的基表,并且在外部参照表的每一行中两者的组合是唯一的。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

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


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

您还可以回答许多更引人注目的问题,而这些问题是使用原始设计无法真正解决的,例如:

  • 卡路里最高/最低的食谱?
  • 大多数食谱中包含的成分?

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

于 2012-07-15T00:50:11.383 回答