检查配方是否含有成分 - MYSQL

时间:2011-04-06 15:38:13

标签: php mysql

嘿大家好我在运行查询/ php组合时遇到了一些麻烦。我似乎只是在我的php中循环遍历内部循环中的太多结果集。我确信有一种更有效的方法。非常感谢任何帮助。

我有一张包含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 x 40(没有找到每种食谱的第一次成分,所以退出)。

我认为我对此的整个方法是错误的,我认为必须有一些我在这里缺少的聪明的SQL。有什么想法吗?我总是可以从我拥有的成分数组中构建一个sql语句......

非常感谢,非常感谢

5 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

您可以使用“IN ANY”类型查询:

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

做到这一点,但基本上已经列出了至少一个用户提供的成分列出的任何食谱,按总成分计数排序,然后只返回超过阈值量的食谱成分存在。

我的门槛可能有点错误 - 偷偷摸摸地怀疑它会计算食谱的成分,而不是用户提供的成分,但其​​余的查询应该是你需要的良好开端。

答案 2 :(得分:0)

问题:为什么你的查询不是直接sql? 您可以通过消除错误的配方进行优化:

  • 首先消除比你的用户成分更多的含量的食谱
  • 通过以下方式进行递归贪心:
    • 选择第一个rid | iid
    • 如果它在用户成分中,请继续,
    • 如果没有,请使用rid =>从Recipe_Ingredients表中删除所有行NEW_TABLE
    • 使用new_table重启|停止new_table count = 0

应该有最好的统计结果。

希望有所帮助

答案 3 :(得分: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),则返回配方。

答案 4 :(得分: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