2

食谱类别

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>

黑森林蛋糕:
自发面粉
牛奶
巧克力
泡打粉

我是新手,所以请原谅任何愚蠢的问题!

4

4 回答 4

5

将多值属性存储在单个逗号分隔的字段中几乎总是一个坏主意。它使一切都很难查询。

相反,您可能需要考虑使用两个新的交集表来重构您的模式。

这两个表保持原样(只是为了不与交集表冲突而将名称更改为)recipe_categorycategories

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表如下,去掉cidiid字段:

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)

然后,您可能希望<br>在应用程序代码中而不是在 SQL 中格式化该结果集(添加 s)。

但是,如果您真的希望在 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 中,您就可以开始了!

于 2010-07-19T01:48:46.217 回答
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
于 2010-07-19T01:47:23.783 回答
1

您绝对不希望像使用cidiidin那样使用逗号分隔的外键列表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

recipe_ingredients

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;

完成此操作后,您可以编写一个查询来连接ingredientsrecipe_ingredients表以获得您想要的结果:

SELECT ingredients.* FROM ingredients, recipe_ingredients WHERE recipe_ingredients.id = 1 AND recipe_ingredients.iid = ingredients.iid;
于 2010-07-19T01:52:48.520 回答
0

你问的事实<br>告诉我你想在网页中显示这些。我的建议是将查询数据与显示数据分开。

由于一个食谱可以有许多成分,并且一种成分可以出现在许多食谱中,因此您需要在食谱和成分之间建立多对多连接表。它将有两列——一列用于配方主键,另一列用于成分主键。

您的逗号分隔值违反了规范化规则。你会想知道那些是什么。

于 2010-07-19T01:48:37.357 回答