查询多个表

时间:2010-07-20 04:35:01

标签: php mysql

继我的查询yesterday之后,我重新构建了我的食谱数据库,如下所示:

类别

cid | category_name
 1  | desserts
 2  | cakes
 3  | biscuits

配方

id | recipe_name
1  | black forest cake
2  | angel cake
3  | melting moments
4  | croquembouche
5  | crepes suzette

成分

iid | ingredient_name
 1  | self-raising flour
 2  | milk
 3  | chocolate
 4  | baking powder
 5  | plain flour
 6  | eggs

recipe_categories

recipe_id | category_id
   1      |    1
   4      |    1
   5      |    1
   1      |    2
   2      |    2
   3      |    3
   4      |    3

recipe_ingredients

recipe_id | ingredient_id
   1      |    1
   2      |    1
   4      |    1
   1      |    2
   2      |    2
   3      |    2
   5      |    2
   1      |    3
   2      |    3
   1      |    4
   3      |    5
   4      |    5

我的查询需要按类别返回配方,其中列出的成分使用
来分隔成分(可能使用GROUP_CONCAT(i.ingredient_name分隔符'
')输出,如昨天建议的那样。)

因此,对甜点的查询将输出:

black forest cake:
self-raising flour
milk
chocolate

croquembouche:
self-raising flour
plain flour

crepes suzette:
milk
plain flour

我知道我必须加入recipe_ingredients和食谱和成分以及搜索类别,但我真的很想如何做到这一点。

3 个答案:

答案 0 :(得分:2)

查询:

SELECT A.recipe_name, GROUP_CONCAT(ingredient_name) AS ingredient_names
FROM recipes A
LEFT JOIN recipe_ingredients B ON A.id = B.recipe_id
LEFT JOIN ingredients C ON B.ingredient_id = C.iid
LEFT JOIN recipe_categories D ON A.id = D.recipe_id
LEFT JOIN categories E ON D.category_id = E.cid
WHERE category_id = <serach_id>
GROUP BY id

结果:

+-------------------+-------------------------------------------------+
| recipe_name       | ingredient_names                                |
+-------------------+-------------------------------------------------+
| black forest cake | chocolate,baking powder,self-raising flour,milk |
| angel cake        | self-raising flour,milk,chocolate               |
| melting moments   | milk,plain flour                                |
| croquembouche     | self-raising flour,plain flour                  |
| crepes suzette    | milk                                            |
+-------------------+-------------------------------------------------+

以下是您要求使用GROUP_CONCAT的内容。每种成分由,

分隔

答案 1 :(得分:1)

我发现你可以通过一次一步地分解它们来理解事物。

首先,让我们抓住我们的甜点。

SELECT categories.cid
  FROM categories
 WHERE category_name = 'desserts'

现在让我们通过将它们粘贴在结果的左侧来抓住所有沙漠的食谱。对于类别中的每个匹配行,recipe_categories中可以有一个或多个匹配的行。对于recipe_categories中的每个匹配行,配方中可以有一个或多个匹配的行。

    SELECT categories.cid, recipes.recipe_name
      FROM categories
 LEFT JOIN recipe_categories ON (recipe_categories.category_id = categories.cid)
 LEFT JOIN recipes ON (recipes.rid = recipe_categories.recipe_id)
     WHERE category_name = 'desserts'

最后,让我们在左边附上我们的食材。对于我们到目前为止的每个匹配行(我们现在都在配方中),我们可以在recipe_ingredients中有一个或多个匹配项,然后在配方中再次匹配一个或多个匹配项。

    SELECT recipes.recipe_name, ingredients.ingredient_name
      FROM categories
 LEFT JOIN recipe_categories ON (recipe_categories.category_id = categories.cid)
 LEFT JOIN recipes ON (recipes.rid = recipe_categories.recipe_id)
 LEFT JOIN recipe_ingredients ON (recipe_ingredients.recipe_id = recipes.rid)
 LEFT JOIN ingredients ON (ingredients.iid = recipe_ingredients.ingredient_id)
     WHERE category_name = 'desserts'

到目前为止我?

现在,已经很晚了,我还没有测试过这个,但它应该返回你正在寻找的数据。事实上,我可能完全使用错误的连接类型来正确完成此操作。如果我错了,我肯定评论中有人会纠正我。

不会做的是返回您希望格式化的数据。让我们在查询的最后一次迭代中添加一些id字段。

    SELECT recipes.rid, recipes.recipe_name, ingredients.iid, ingredients.ingredient_name
      FROM categories
 LEFT JOIN recipe_categories ON (recipe_categories.category_id = categories.cid)
 LEFT JOIN recipes ON (recipes.rid = recipe_categories.recipe_id)
 LEFT JOIN recipe_ingredients ON (recipe_ingredients.recipe_id = recipes.rid)
 LEFT JOIN ingredients ON (ingredients.iid = recipe_ingredients.ingredient_id)
     WHERE category_name = 'desserts'

让我们将数据收集到一个多维数组中。让我们假装我们正在使用PDO。

$query = '...';
$sh = $db->prepare($query);
$sh->execute();
$recipes = array();
while($row = $sh->fetch(PDO::FETCH_ASSOC)) {
// For each row in the result set, check to see if we've looked at this recipe.
    if(!array_key_exists( $row['rid'], $recipes )) {
    // If we haven't, let's initialize the row with the recipe name
    // and a place to stick each ingredient.
        $recipes[ $row['rid'] ] = array( 
            'recipe_name' => $row['recipe_name'],  
            'ingredients' => array()
        );
    }
// Place this ingredient with the proper recipe.
    $recipes[ $row['rid'] ]['ingredients'][ $row['iid'] ] = $row['ingredient_name'];
}

结果应该是这样的:

array(
    1 => array(
        'recipe_name' => 'black forest cake',
        'ingredients' => array(
            '1' => 'self-raising flour',
            '2' => 'milk',
            ...
        )
    )
)

从这个结构中,你可以做你想做的事。

答案 2 :(得分:0)

我会留给你弄清楚如何正确地格式化它,但是要开始你需要一起加入成分和食谱表,但搜索类别(也加入)。

我发现执行后者的最简单/最好的方法是使用IN子句(EXISTS也可以)。例如:

SELECT *
FROM recipes
LEFT OUTER JOIN recipe_ingredients ON recipe.recipe_id = recipe_ingredients.recipe_id
WHERE recipes.id IN (SELECT recipe_categories.recipe_id
                     FROM categories
                     INNER JOIN recipe_categories ON categories.cid = recipe_categories.category_id
                     WHERE categories.category_name = @search_term)

请注意,除非您只搜索一个类别,否则当食谱包含多个匹配类别时,执行直接JOIN将生成多行,因此IN / EXISTS最佳。