21

次の表を考えます。

Recipes
| id | name
| 1  | 'chocolate cream pie'
| 2  | 'banana cream pie'
| 3  | 'chocolate banana surprise'

Ingredients
| id | name
| 1  | 'banana'
| 2  | 'cream'
| 3  | 'chocolate'

RecipeIngredients
| recipe_id | ingredient_id
|     1     |      2
|     1     |      3
|     2     |      1
|     2     |      2
|     3     |      1
|     3     |      3

components.name = 'chocolate' および components.name = 'cream' であるレシピを検索する SQL クエリを作成するにはどうすればよいですか?

4

6 に答える 6

15

使用する:

  SELECT r.name
    FROM RECIPES r
    JOIN RECIPEINGREDIENTS ri ON ri.recipe_id = r.id
    JOIN INGREDIENTS i ON i.id = ri.ingredient_id
                      AND i.name IN ('chocolate', 'cream')
GROUP BY r.name
  HAVING COUNT(DISTINCT i.name) = 2

ここでの重要なポイントは、カウントが成分名の数と等しくなければならないということです。個別のカウントではない場合、重複による誤検知のリスクがあります。

于 2010-06-12T17:15:40.957 に答える
11

これを関係分割といいます。ここでは、さまざまなテクニックについて説明します

まだ与えられていない代替手段の 1 つは、二重の NOT EXISTS です。

SELECT r.id, r.name
FROM Recipes r
WHERE NOT EXISTS (SELECT * FROM Ingredients i
                  WHERE name IN ('chocolate', 'cream')
                  AND NOT EXISTS
                      (SELECT * FROM RecipeIngredients ri
                       WHERE ri.recipe_id = r.id
                       AND ri.ingredient_id = i.id))
于 2010-06-12T22:52:22.353 に答える
4

複数の関連付けを検索する場合、クエリを作成する最も簡単な方法EXISTSは、単一のストレートの代わりに複数の条件を使用することJOINです。

SELECT r.id, r.name
FROM Recipes r
WHERE EXISTS
(
    SELECT 1
    FROM RecipeIngredients ri
    INNER JOIN Ingredients i
        ON i.id = ri.ingredient_id
    WHERE ri.recipe_id = r.id
    AND i.name = 'chocolate'
)
AND EXISTS
(
    SELECT 1
    FROM RecipeIngredients ri
    INNER JOIN Ingredients i
        ON i.id = ri.ingredient_id
    WHERE ri.recipe_id = r.id
    AND i.name = 'cream'
)

関連付けが一意であることが確実にわかっている場合 (つまり、1 つのレシピが各材料のインスタンスを 1 つしか持てない場合)、COUNT関数でグループ化サブクエリを使用して少しごまかすことができ、場合によっては高速化できます (パフォーマンスはDBMS):

SELECT r.id, r.Name
FROM Recipes r
INNER JOIN RecipeIngredients ri
    ON ri.recipe_id = r.id
INNER JOIN Ingredients i
    ON i.id = ri.ingredient_id
WHERE i.name IN ('chocolate', 'cream')
GROUP BY r.id, r.Name
HAVING COUNT(*) = 2

または、レシピに同じ材料の複数のインスタンスが含まれる可能性がある場合 (関連付けテーブルUNIQUEに制約がない場合RecipeIngredients)、最後の行を次のように置き換えることができます。

HAVING COUNT(DISTINCT i.name) = 2
于 2010-06-12T17:13:03.033 に答える
2
select r.*
from Recipes r
inner join (
    select ri.recipe_id
    from RecipeIngredients ri 
    inner join Ingredients i on ri.ingredient_id = i.id
    where i.name in ('chocolate', 'cream')
    group by ri.recipe_id
    having count(distinct ri.ingredient_id) = 2
) rm on r.id = rm.recipe_id
于 2010-06-12T17:14:37.230 に答える
1
SELECT DISTINCT r.id, r.name
FROM Recipes r
INNER JOIN RecipeIngredients ri ON
    ri.recipe_id = r.id
INNER JOIN Ingredients i ON
    i.id = ri.ingredient_id
WHERE
    i.name IN ( 'cream', 'chocolate' )

次のコメントを編集しました、ありがとう!これが正しい方法です:

SELECT DISTINCT r.id, r.name
FROM Recipes r
INNER JOIN RecipeIngredients ri ON
    ri.recipe_id = r.id
INNER JOIN Ingredients i ON
    i.id = ri.ingredient_id AND
    i.name = 'cream'
INNER JOIN Ingredients i2 ON
    i2.id = ri.ingredient_id AND
    i2.name = 'chocolate'
于 2010-06-12T17:16:02.967 に答える
1

別の方法:

バージョン 2 (ストアド プロシージャとして) 改訂

select   r.name
from   recipes r
where   r.id  = (select  t1.recipe_id
        from  RecipeIngredients t1 inner join
     RecipeIngredients     t2 on t1.recipe_id = t2.recipe_id
     and     t1.ingredient_id = @recipeId1
     and     t2.ingredient_id = @recipeId2)

編集 2: [人々が叫び始める前に] :)

これは、バージョン 2 の先頭に配置できます。これにより、ID を渡す代わりに名前でクエリを実行できます。

select @recipeId1 = recipe_id from Ingredients where name = @Ingredient1
select @recipeId2 = recipe_id from Ingredients where name = @Ingredient2

バージョン 2 をテストしましたが、動作します。この場合、Ingredient テーブルにリンクするほとんどのユーザーはまったく必要ありませんでした。

編集 3: (テスト結果);

このストアド プロシージャを実行すると、次の結果が得られます。

結果の形式は次のとおりです (最初の Recipe_id ; 2 番目の Recipe_id, Result)

1,1, Failed
1,2, 'banana cream pie'
1,3, 'chocolate banana surprise'
2,1, 'banana cream pie'
2,2, Failed
2,3, 'chocolate cream pie'
3,1, 'chocolate banana surprise'
3,2, 'chocolate cream pie'
3,3, Failed

明らかに、このクエリは、両方の制約が同じ場合は処理しませんが、他のすべてのケースでは機能します。

編集4:(同じ制約ケースの処理):

この行を置き換えます:

r.id = (select t1...

r.id in (select t1...

失敗したケースで動作して、以下を提供します。

1,1, 'banana cream pie' and 'chocolate banana surprise'
2,2, 'chocolate cream pie' and 'banana cream pie'
3,3, 'chocolate cream pie' and 'chocolate banana surprise'
于 2010-06-12T23:18:54.183 に答える