我有一张包含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语句......
非常感谢,非常感谢
答案 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? 您可以通过消除错误的配方进行优化:
应该有最好的统计结果。
希望有所帮助
答案 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