什么是用于此数据库建模/设计的最佳方法?

时间:2012-07-15 00:20:49

标签: php mysql database-design

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

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

因此,我提出的存储成分选择的方法是:

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

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

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

  4. 所以问题是:这种方法是否是最佳的,如果不是,那么更好的方法是什么呢?

2 个答案:

答案 0 :(得分:2)

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

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

另外,如果你将它们以逗号分隔的字符串存储,你会如何将单一成分的卡路里计数与成分联系起来?

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

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

有许多配方含有许多成分,因此创建一个表格,将成分与特定配方联系起来,这使配料与食谱的关系可以多对多,而不是多对多。

recipe_ingredients
------------------
recipe_id
ingredient_id

你应该在这里阅读normalization

答案 1 :(得分: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字符串中的正确位置,或者您需要更新表中的每一行以反映单个成分中的最微小变化。

您还可以回答更多令人信服的问题,否则您无法使用原始设计,例如:

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

......列表继续,实施此设计的好处将为您提供良好的服务。通过正确的方式做事,你将使自己免于遭受巨大的困难和痛苦。 =)