2011-04-06 41 views
4

大家好。我在運行查詢/ php組合時遇到了一些麻煩。我似乎只是在我的php循環內循環中的太多結果集。我確信有一個更有效的方法來做到這一點。非常感謝任何幫助。檢查食譜中是否含有成分 - MYSQL

我有持有3500個食譜([配方])的表:
rid | recipe_name

而且保存600種不同的成分([配料])
iid | i_name

每個另一個表配方有x個關聯的成分,我用一個不錯的連接表來創建關聯([recipe_ingredients])
uid | rid | iid
(其中uid是隻爲表的唯一ID)

例如:

rid: 1 | recipe_name: Lemon Tart 
..... 
iid: 99 | i_name: lemon curd 
iid: 154 | i_name: flour 
..... 
1 | 1 | 99 
2 | 1 | 154 

我試圖運行查詢,允許用戶輸入他們有什麼成分,它會告訴你任何你可以用這些成分。它不必使用所有的配料,但你需要擁有配方的所有成分。例如,如果我有面粉,雞蛋,鹽,牛奶和檸檬醬,我可以做'煎餅','檸檬餡餅'(如果我們假設檸檬撻沒有其他成分:)),但不能使'燴飯'(因爲我沒有米飯,或者其他任何需要的東西)。

在我的PHP我有一個數組包含用戶擁有的所有成分。目前他們的方式我正在運行這是通過每個配方(循環1),然後檢查該配方中的所有成分,以查看每種成分是否包含在我的成分數組中(循環2)。只要它在配方中找到一種配料,並不在我的配方中,它就會顯示「否」並進入下一個配方。如果是這樣,它將把rid存儲在一個新的數組中,我稍後使用它來顯示結果。

但是,如果我們看一下效率,如果我假設3500個食譜,並且我的數組中有40種成分,最壞的情況是它運行3500 x 40n,其中n =配方中成分的數量。最好的情況仍然是3500×40(沒有找到一個配料第一次爲每個配方如此退出)。

我認爲我的整個方法是錯誤的,我認爲這裏必須有一些聰明的SQL。有什麼想法嗎?我總是可以從配料陣列我有建立一個SQL語句......

非常感謝提前,大加讚賞

回答

2

我建議存儲成分在配方表的配方的數量的計數,只是爲了效率的緣故(它會使查詢,如果沒有它來計算更快這個信息每次)。這是非規範化,這對數據完整性不利,但對性能有好處。您應該知道,如果更新配方,這可能會導致數據不一致,並且您不小心確保在每個相關位置更新數字。我假設你已經在配方表中將新列設置爲ing_count。

如果它們是通過用戶輸入提供的,請確保您轉義NAME1,NAME2等中的值 - 否則您將面臨SQL注入風險。

select recipe.rid, recipe.recipe_name, recipe.ing_count, count(ri) as ing_match_count 
from recipe_ingredients ri 
inner join (select iid from ingredients where i.name='NAME1' or i.name='NAME2' or i.NAME='NAME3') ing 
on ri.iid = ing.iid 
inner join recipe 
on recipe.rid = ri.rid 
group by recipe.rid, recipe.recipe_name, recipe.ing_count 
having ing_match_count = recipe.ing_count 

如果你不想要存儲的配方數量,你可以做這樣的事情:

select recipe.rid, recipe.recipe_name, count(*) as ing_count, count(ing.iid) as ing_match_count 
from recipe_ingredients ri 
inner join (select iid from ingredients where i.name='NAME1' or i.name='NAME2' or i.NAME='NAME3') ing 
on ri.iid = ing.iid 
right outer join recipe 
on recipe.rid = ri.rid 
group by recipe.rid, recipe.recipe_name 
having ing_match_count = ing_count 
+0

是的,我確實在表格中存儲了配方的配料數量,並注意規範化(或缺乏)。不完全確定只是通過閱讀查詢「ing」創建的表上的連接,但會運行並讓您知道...... – 2011-04-06 16:11:15

+0

ing表上的連接是如何篩選成分列表。因爲它是一個內部連接,任何與您提供的列表不匹配的配方都將退出,您不必將這些配料連接到其他表格。 – schizodactyl 2011-04-06 16:23:00

+0

請注意,您可以使用IN ANY查詢,就像其他地方所討論的一樣,但就執行速度而言,查詢將是相同的 - 它主要只是語義。我傾向於避免在任何查詢中使用,因爲它們不能用於準備好的語句,儘管準備好的語句在您的情況下不是很有用,因爲成分的數量是可變的。 – schizodactyl 2011-04-06 16:25:13

1

,你可以在「在任何」類型的查詢:

select recipes.rid, count(recipe_ingredients.iid) as cnt 
from recipes 
left join recipe_ingredients on recipes.rid = recipe_ingredients.rid 
where recipes_ingredients in any (the,list,of,ingredients,the,user,hash) 
group by recipes.rid 
having cnt > some_threshold_amount 
order by cnt desc 

從我的頭頂開始做這件事,但基本上將所有用戶提供的成分列出的食譜中的任何食譜抽出,按總成分計數排序,然後只返回超過閾值量的食譜的成分存在。

我可能得到了一個錯誤的閾值 - 偷偷摸摸的懷疑它會計算食譜的成分,而不是用戶提供的成分,但其​​餘的查詢應該是您需要的良好開端。

+0

我看你在說什麼 - 但我不明白數位的重點。 IN ANY將減少初始配方,僅顯示至少含有1種配料的配方(從3500減少到少量配料),但配方中配料的數量對真實而言是多餘的問題在手。 如果一個配方有2個或12個成分,它仍然需要檢查。只是有點困惑的「threshold_amount」。 – 2011-04-06 15:52:53

+0

該解決方案實際上並未解決問題。提問者只需要在列表中包含所有成分的食譜,同時這將返回在列表中的任何成分上方具有某些成分的食譜。 – schizodactyl 2011-04-06 15:56:07

+0

@Schizodactyl:其實,沒有。如果櫥櫃裏有20個罐子的東西,我不想要一個全部使用20的配方。我想要一個使用至少一個,最好是3個或10個的配方。因此,在閾值的嘗試數。 「我有以下40種成分,並且希望使用至少5種食譜的食譜」。 – 2011-04-06 16:16:11

0

問題:爲什麼不是你的查詢直接sql? 您可以通過消除錯誤的配方優化:

  • 首先排除有更多的ingridients比你的用戶配料配方
  • 進行遞歸貪心者:
    • 挑頭去掉| IID
    • 如果它在用戶成分中,則繼續,如果不是,則從rid => new_table中清除Recipe_Ingredients表中的所有行
    • 重新啓動使用new_table |停止NEW_TABLE計數= 0

應該有最好的統計結果。

希望它幫助

0

事情是這樣的:

SELECT r.*, COUNT(ri.iid) AS count FROM recipe r 
    INNER JOIN recipe_ingredient ri ON r.rid = ri.rid 
    INNER JOIN ingredient i ON i.iid = ri.iid 
    WHERE i.name IN ('milk', 'flour') 
    GROUP BY r.rid 
    HAVING count = 2 

這是很容易理解的。 count保存列表中的成分數量(牛奶,麪粉),這些成分與每種配方相匹配。如果count與WHERE子句中的成分數量(在此例中爲2)匹配,則返回配方。

+0

不會工作,那將只返回包含我說我有的所有成分的食譜。如果我的櫥櫃裏有40種食材,我不想展示一個包含全部40種食譜的食譜,但我可以從40種食譜中做出食譜。 – 2011-04-06 16:07:34

0
SELECT irl.ingredient_amount, r . * , i.thumbnail 
FROM recipes r 
LEFT JOIN recipe_images i ON (i.recipe_id = r.recipe_id) 
LEFT JOIN ingredients_recipes_link irl ON (irl.recipe_id = r.recipe_id) 
WHERE irl.recipe_id 
IN (

SELECT recipe_id 
FROM `ingredients_recipes_link` 
WHERE ingredient_id 
IN (24, 21, 22) 
HAVING count(*) =3 
) 
GROUP BY r.recipe_id