CodeIgniter 按逗号分隔分类ID数组查询食品数据的去重与性能优化
适配逗号分隔category_id的无重复高性能查询实现
核心问题根源
原有循环遍历分类ID单独查库的实现存在两个明确缺陷:
- 多次发起数据库请求,造成不必要的IO性能损耗
- 每个分类独立返回结果块,同一食品匹配多个分类时会在不同结果块中重复出现,最终形成嵌套冗余数组
优化实现方案
将分散的FIND_IN_SET条件合并为单次查询的OR匹配逻辑,把多次数据库请求压缩为1次,从根源上避免结果重复。
完整可运行代码
if (count($categoryIds) > 0 && count($allergyIds) == 0 ) { // 提前对传入分类ID做整数转换,从根源防范SQL注入 $safeCategoryIds = array_map('intval', $categoryIds); $matchConditions = []; foreach ($safeCategoryIds as $singleCatId) { $matchConditions[] = "FIND_IN_SET($singleCatId, food.category_id)"; } // 用OR拼接所有匹配条件,外层加括号避免和后续AND条件逻辑冲突 $combinedMatch = '(' . implode(' OR ', $matchConditions) . ')'; return $this->db->select('food.food_id,food_name,food_image,food_thumbnail,description,food_variations.price as price,is_active') ->from('food') ->join('food_variations', 'food_variations.food_id = food.food_id') // 第三个参数传FALSE表示不自动给这个条件加引号转义,因为我们已经提前做了安全校验 ->where($combinedMatch, NULL, FALSE) ->where('food.restaurant_id', $restaurantId) ->where('food.is_active', '1') ->where('food.is_deleted', '0') ->where('food.food_status','3') ->where('food_variations.food_variation_id = food.default_food_variation_id') ->get() ->result_array(); }
实现效果说明
- 查询仅执行1次,相比原来循环N个分类就查N次的模式,性能随分类数量提升线性优化
- 返回结果为扁平的一维食品条目数组,同一食品无论匹配多少个传入的分类ID,都只会返回一次,完全消除嵌套重复的问题
- 逻辑和之前单值category_id时用
where_in的查询逻辑完全对齐,不会出现条件优先级错位的问题
兜底注意事项
- 如果数据存在异常(比如food_variations关联重复导致条目重复),可以在select字段后加
DISTINCT做去重兜底,修改select部分为:$this->db->select('DISTINCT food.food_id,food_name,food_image,food_thumbnail,description,food_variations.price as price,is_active') - 必须保留对传入分类ID的整数转换步骤,绝对不能直接把未校验的输入拼进SQL语句,避免注入风险
内容的提问来源于stack exchange,提问作者Rishi Saravadaa
相关产品推荐
相关产品推荐

