这是一个简单的 N:M(多对多)关系,您提出的方法可能会在效率和管理方面造成灾难。
这是你的情况:
- 您有两个实体:
recipes
和ingredients
。
- 一种成分可能是许多食谱的一部分。
- 一个食谱可能由许多成分组成。
每当您在任何两个实体之间建立这种关系时,您都不会想要两个,而是三个表:
+-----------+ +-------------------------+ +-------------------+
| 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 字符串中的正确位置,或者您需要更新表中的每一行以反映单个成分中最轻微的变化。
您还可以回答许多更引人注目的问题,而这些问题是使用原始设计无法真正解决的,例如:
- 卡路里最高/最低的食谱?
- 大多数食谱中包含的成分?
...不胜枚举,实施此设计的好处将为您提供良好的服务。通过正确的方式做事,您将避免自己遭受很多困难和痛苦。=)