m:n多对多关联表下如何用单条SQL查询匹配全部输入食材的食谱
$recipeFilter = implode(' AND ', $recipeFilters);
...
# 非最优解决方案 我知道可以为每个食材单独查询,再通过数组操作得到想要的结果。 **是否可以用单条SQL实现该需求,具体要怎么写?** --- ### 解决方案 #### 核心思路 现有方案2出错的原因是`AND`要求同一个食材同时匹配所有关键词,不符合业务逻辑。正确逻辑为:先筛选出匹配任意一个搜索关键词的食材关联的食谱,再按食谱ID分组,统计该食谱匹配到的不同食材的数量,数量等于搜索关键词总数时,即说明该食谱覆盖了所有搜索要求。 #### 完整实现代码 ```php // 统计搜索关键词总数 $filterCount = count($filterParams); // 生成OR匹配条件,这里先对参数做简单转义避免注入,后续建议用预处理方案 $recipeFilters = array_map(function ($param) { return "ri.description LIKE '%" . addslashes($param) . "%'"; }, $filterParams); $recipeFilter = implode(' OR ', $recipeFilters); $selRecipes = <<<SQL SELECT rr.id FROM recipe_ingredient ri LEFT JOIN recipe_intermediate_ingredient_recipe riir ON riir.ingredient_id = ri.id LEFT JOIN recipe_recipe rr ON rr.id = riir.recipe_id WHERE {$recipeFilter} AND rr.id IS NOT NULL GROUP BY rr.id -- 匹配到的不同食材数等于输入关键词总数即符合要求 HAVING COUNT(DISTINCT ri.id) = {$filterCount} SQL; $recipes = data_select($selRecipes); // 直接用array_column替代循环提取id,写法更简洁 $ids = array_column($recipes, 'id');
安全优化建议
现有直接拼接SQL字符串的写法存在SQL注入风险,建议修改为预处理绑定参数的形式:
// 生成带占位符的条件 $recipeFilters = array_fill(0, $filterCount, "ri.description LIKE ?"); $recipeFilter = implode(' OR ', $recipeFilters); // 生成带前后模糊匹配的绑定参数 $bindParams = array_map(function($param) { return "%{$param}%"; }, $filterParams); // 可修改自定义的data_select函数支持传入绑定参数,通过mysqli_stmt_bind_param完成参数绑定即可
内容的提问来源于stack exchange,提问作者s3c
相关产品推荐
相关产品推荐

