MySQL三表关联查询:筛选不含用户禁用食材的食谱并兼容空列表
解决MySQL多表关联筛选无禁用食材食谱的问题
我来帮你搞定这个查询问题!你的需求是筛选出完全不包含用户禁用食材的食谱,同时还要支持“用户无禁用食材时返回所有食谱”的场景,咱们一步步来梳理。
先分析你当前SQL的问题
你现在的SQL语句逻辑搞反了:
SELECT RecipeID, RecipeName FROM recipes LEFT JOIN ingredients INNER JOIN usersList ON ingredients.IngredientID = usersList.IngredientID ON recipes.RecipeID = ingredients.RecipeID WHERE recipes.RecipeID IS NOT NULL AND usersList.userID = 2 GROUP BY recipes.RecipeID
这里的INNER JOIN usersList会先把所有被用户2禁用的食材关联出来,再和recipes做LEFT JOIN,最终拿到的是包含禁用食材的食谱,正好和你想要的结果相反,而且JOIN的顺序和条件位置也容易导致逻辑混乱。
两种正确的解决方案
方案1:使用NOT EXISTS子查询(逻辑清晰,性能友好)
这种写法最直观,核心逻辑是:找出所有不存在任何被用户禁用食材的食谱。
SELECT r.RecipeID, r.RecipeName FROM recipes r WHERE NOT EXISTS ( -- 子查询:检查当前食谱是否包含用户2禁用的食材 SELECT 1 FROM ingredients i JOIN usersList ul ON i.IngredientID = ul.IngredientID WHERE i.RecipeID = r.RecipeID AND ul.userID = 2 )
- 当用户2没有禁用食材时,子查询返回空结果,
NOT EXISTS判定为真,会返回所有食谱; - 当用户2有禁用食材时,会自动排除所有包含这些食材的食谱,正好得到你想要的
Minute Steak。
方案2:使用LEFT JOIN + IS NULL(适合理解JOIN逻辑的场景)
通过左关联禁用食材表,筛选出没有匹配到禁用食材的食谱:
SELECT DISTINCT r.RecipeID, r.RecipeName FROM recipes r LEFT JOIN ingredients i ON r.RecipeID = i.RecipeID LEFT JOIN usersList ul ON i.IngredientID = ul.IngredientID AND ul.userID = 2 -- 把用户条件放在ON里,避免过滤掉无禁用食材的食谱 WHERE ul.IngredientID IS NULL
- 这里用
LEFT JOIN关联usersList,并且把userID=2的条件放在ON子句中,而不是WHERE,这样即使用户没有禁用食材,所有食谱的ul字段都会是NULL; WHERE ul.IngredientID IS NULL会筛选出完全没有匹配到禁用食材的食谱;- 加上
DISTINCT是因为一个食谱有多个食材时,LEFT JOIN会生成多条重复记录,需要去重。
总结
两种方案都能满足你的需求,其中NOT EXISTS在数据量较大时性能通常更优,因为它不需要关联所有记录再去重,一旦找到匹配的禁用食材就会停止查询。
内容的提问来源于stack exchange,提问作者w3shivers
相关产品推荐
相关产品推荐

