cid | category
1 | desserts
2 | cakes
3 | biscuits
id | recipe_name | cid | iid
1 | black forest cake | 1,2 | 1,2,3,4
2 | angel cake | 2 | 1,2,4
3 | melting moments | 3 | 2,5
4 | croquembouche | 1,3 | 1,5
iid | ingredient_name
1 | self-raising flour
2 | milk
3 | chocolate
4 | baking powder
5 | plain flour
我可以使用cid
查询数据库来提取某些食谱,即。甜点:
SELECT * FROM recipe_name WHERE cid='1'
但是,如何创建一个如下所示的成分列表,其中成分与<br>
一起列出?
黑森林蛋糕:
自筹面粉
牛奶
巧克力
发酵粉
我是新手,所以请原谅任何愚蠢的问题!
答案 0 :(得分:5)
在单个逗号分隔字段中存储多值属性几乎总是一个坏主意。这使得查询一切都很困难。
相反,您可能需要考虑使用两个新的交集表来重构您的架构。
这两个表保持不变(只是将recipe_category
的名称更改为categories
,以免与交集表冲突):
CREATE TABLE categories (
cid int NOT NULL PRIMARY KEY,
category_name varchar(50)
) ENGINE=INNODB;
CREATE TABLE ingredients (
iid int NOT NULL PRIMARY KEY,
ingredient_name varchar(50)
) ENGINE=INNODB;
修改recipe_name
表,如下所示,删除cid
和iid
字段:
CREATE TABLE recipe_name (
id int NOT NULL PRIMARY KEY,
recipe_name varchar(50)
) ENGINE=INNODB;
然后,您可以使用以下两个交集表定义多值关系:
CREATE TABLE recipe_ingredients (
recipe_id int NOT NULL,
ingredient_id int NOT NULL,
PRIMARY KEY (recipe_id, ingredient_id),
FOREIGN KEY (recipe_id) REFERENCES recipe_name (id),
FOREIGN KEY (ingredient_id) REFERENCES ingredients (iid)
) ENGINE=INNODB;
CREATE TABLE recipe_categories (
recipe_id int NOT NULL,
category_id int NOT NULL,
PRIMARY KEY (recipe_id, category_id),
FOREIGN KEY (recipe_id) REFERENCES recipe_name (id),
FOREIGN KEY (category_id) REFERENCES categories (cid)
) ENGINE=INNODB;
现在让我们用您的示例数据填充这些表:
INSERT INTO categories VALUES (1, 'desserts');
INSERT INTO categories VALUES (2, 'cakes');
INSERT INTO categories VALUES (3, 'biscuits');
INSERT INTO ingredients VALUES(1, 'self-raising flour');
INSERT INTO ingredients VALUES(2, 'milk');
INSERT INTO ingredients VALUES(3, 'chocolate');
INSERT INTO ingredients VALUES(4, 'baking powder');
INSERT INTO ingredients VALUES(5, 'plain flour');
INSERT INTO recipe_name VALUES(1, 'black forest cake');
INSERT INTO recipe_name VALUES(2, 'angel cake');
INSERT INTO recipe_name VALUES(3, 'melting moments');
INSERT INTO recipe_name VALUES(4, 'croquembouche');
要定义配方及其成分和类别之间的关系,您需要按如下方式填写交集表:
INSERT INTO recipe_categories VALUES (1, 1);
INSERT INTO recipe_categories VALUES (1, 2);
INSERT INTO recipe_categories VALUES (2, 2);
INSERT INTO recipe_categories VALUES (3, 3);
INSERT INTO recipe_categories VALUES (4, 1);
INSERT INTO recipe_categories VALUES (4, 3);
INSERT INTO recipe_ingredients VALUES (1, 1);
INSERT INTO recipe_ingredients VALUES (1, 2);
INSERT INTO recipe_ingredients VALUES (1, 3);
INSERT INTO recipe_ingredients VALUES (1, 4);
INSERT INTO recipe_ingredients VALUES (2, 1);
INSERT INTO recipe_ingredients VALUES (2, 2);
INSERT INTO recipe_ingredients VALUES (2, 3);
INSERT INTO recipe_ingredients VALUES (3, 2);
INSERT INTO recipe_ingredients VALUES (3, 5);
INSERT INTO recipe_ingredients VALUES (4, 1);
INSERT INTO recipe_ingredients VALUES (4, 5);
最后,构建查询将非常简单:
SELECT i.ingredient_name
FROM recipe_ingredients ri
JOIN ingredients i ON (i.iid = ri.ingredient_id)
WHERE ri.recipe_id = (SELECT id
FROM recipe_name
WHERE recipe_name = 'Black Forest Cake');
结果:
+--------------------+
| ingredient_name |
+--------------------+
| self-raising flour |
| milk |
| chocolate |
| baking powder |
+--------------------+
4 rows in set (0.00 sec)
然后,您可能希望在应用程序代码中而不是在SQL中格式化该结果集(添加<br>
)。
但是如果你真的希望在SQL中这样做,那么MySQL支持方便的GROUP_CONCAT()
函数,可以按如下方式使用:
SELECT GROUP_CONCAT(i.ingredient_name separator '<BR>') output
FROM recipe_ingredients ri
JOIN ingredients i ON (i.iid = ri.ingredient_id)
WHERE ri.recipe_id = (SELECT id
FROM recipe_name
WHERE recipe_name = 'Black Forest Cake');
结果:
+----------------------------------------------------------+
| output |
+----------------------------------------------------------+
| self-raising flour<BR>milk<BR>chocolate<BR>baking powder |
+----------------------------------------------------------+
1 row in set (0.00 sec)
将其转储到HTML中,你很高兴!
答案 1 :(得分:1)
首先你应该使用交叉表。在此表中,您将获得recipe_names的键列表以及每个成分的键。因此,对于黑森林蛋糕,您将在交叉表中有4行。这使您可以更快速,更轻松地构建查询。你不必乱用密钥数组。
对于黑森林蛋糕,表格看起来像这样:
recipe_name_id ingredients_id
1 1
1 2
1 3
1 4
查询将如下所示:
SELECT ingredient_name
FROM recipe_name
, ingredients
, recipe_ingredients --intersect table
WHERE recipe_name.id=1
AND recipe_name.id = recipe_ingredients.recipe_name_id
AND ingredients.id = recipe_ingredients.ingredients_id
答案 2 :(得分:1)
您绝对不希望像cid
中使用iid
和recipe_name
那样使用以逗号分隔的外键列表。相反,创建只处理这些关系的表:
id | recipe_name
1 | black forest cake
2 | angel cake
3 | melting moments
4 | croquembouche
id | cid
1 | 1
1 | 2
2 | 2
3 | 3
4 | 1
4 | 3
id | iid
1 | 1
1 | 2
1 | 3
1 | 4
2 | 1
2 | 2
2 | 4
3 | 2
3 | 5
4 | 1
4 | 5
现在您的查询选择任何甜点(您的旧甜点不起作用,因为它会错过cid
1,2
的行,例如)看起来像:
SELECT recipe_name.* FROM recipe_name, recipe_categories WHERE recipe_name.id = recipe_categories.id AND recipe_categories=cid = 1;
完成后,您可以编写一个连接ingredients
和recipe_ingredients
表的查询,以获得您想要的结果:
SELECT ingredients.* FROM ingredients, recipe_ingredients WHERE recipe_ingredients.id = 1 AND recipe_ingredients.iid = ingredients.iid;
答案 3 :(得分:0)
您询问<br>
的事实告诉我您要在网页中显示这些内容。我的建议是将数据的查询与显示分开。
由于食谱中含有多种成分,而且许多配方中都含有一种成分,因此您需要在配方和配料之间加入多对多的配料。它将有两列 - 一列用于配方主键,另一列用于配料主键。
您的逗号分隔值会中断normalization rules。你会想知道它们是什么。