2015-09-27 34 views
0

我想創建一個查詢,返回一組特定的寄存器,但我嘗試了幾種方法,它不工作。SQL查詢不清楚

我有像下面4個表:

recipe { 
    idRecipe integer, 
    name varchar(100) 
} 

recipeStep { 
    idStep integer, 
    idRecipe integer, 
    instruction varchar(100) 
} 

recipeIngredient { 
    idStep integer, 
    idIngredient integer 
} 

ingredient { 
    idIngredient integer, 
    name varchar(100) 
} 
  • 配方可具有一個或多個步驟;
  • 一步可以只有一個配方的一部分;
  • 一步可以有一個或多個成分;
  • 一種成分可以是一個或多個步驟的一部分;

該過濾器將返回包含用戶選擇的成分的所有配方。可以說我想要所有食譜含有所有食譜內涵成分68,25,36,109。於是,我來到了這一點:

SELECT r.* 
    FROM recipe r, recipeStep rs, recipeIngredient ri 
WHERE r.idRecipe = rs.idRecipe 
    AND rs.idStep = ri.idStep 
    AND ri.idIngredient in (68,25,36,109) 

的問題是,該查詢返回包含任何這些成分的所有配方,我只希望所有配方包含他們都做,這是

+0

您顯示的查詢可能會返回每個食譜多次,因爲它有匹配的成分 - 所以你可以按食譜(id)分組,並計算不同的成分,看看它們是否足夠(在你的情況下是4)。 – jkavalik

+0

@ jkavalik。我正要給相同的建議=) – Jacobian

回答

2

的一種方式使用group by子句和having子句,該子句將組限制爲具有4種不同成分計數的子句。

SELECT r.idRecipe, r.name 
    FROM recipe r 
    JOIN recipeStep rs ON r.idRecipe = rs.idRecipe 
    JOIN recipeIngredient ri ON rs.idStep = ri.idStep 
WHERE ri.idIngredient IN (68,25,36,109) 
GROUP BY r.idRecipe, r.name 
HAVING COUNT(DISTINCT ri.idIngredient) = 4; 

另外,您可以選擇跳過謂詞和having子句中使用條件聚集:

SELECT r.idRecipe, r.name 
    FROM recipe r 
    JOIN recipeStep rs ON r.idRecipe = rs.idRecipe 
    JOIN recipeIngredient ri ON rs.idStep = ri.idStep 
GROUP BY r.idRecipe, r.name 
HAVING SUM(CASE WHEN ri.idIngredient = 68 THEN 1 END) > 0 
    AND SUM(CASE WHEN ri.idIngredient = 25 THEN 1 END) > 0 
    AND SUM(CASE WHEN ri.idIngredient = 36 THEN 1 END) > 0 
    AND SUM(CASE WHEN ri.idIngredient = 109 THEN 1 END) > 0; 

兩個查詢將允許額外的成分,只要配方具有四個強制的。如果你想要完全匹配,你可以在where子句中添加另一個條件。

+0

我猜測第二個查詢效率不高,因爲它不能在'ri.idIngredient'上使用索引,因此它將執行全表掃描。 – jkavalik

+0

@jkavalik這可能是真的,如果where子句放回到第二個查詢中,它應該能夠使用索引。我只是想表現出另一種方式。 – jpw

+0

這很有趣,但如果你可以請添加WHERE,並且可能刪除GROUP BY中的'r.name',因爲它是冗餘的,並且也會降低速度。 (在激活「ONLY_FULL_GROUP_BY」之前,需要在5.6之前使用它,但是在沒有它的情況下或在5.7中可以檢測到函數依賴關係) – jkavalik