Laravel Eloquent使用FIND_IN_SET查询数组匹配逗号分隔字段方案
Laravel Eloquent 多值匹配逗号分隔字段查询及排序实现
问题根因
- MySQL原生
FIND_IN_SET()函数第一个参数仅支持传入单个字符串/数字值,直接传入数组会触发SQL语法错误,无法得到预期匹配结果 - 该场景需要实现两个核心需求:匹配关联表
size_ids字段包含传入size_id数组任意值的Offer记录;支持按传入的size数组规则对结果排序
可落地实现代码
所有参数使用PDO占位符绑定,避免SQL注入风险,代码兼容Laravel 6+所有版本:
// 预处理入参,过滤非法值,统一转成整型 $sizeIds = collect($input['size_id']) ->filter(fn($id) => is_numeric($id)) ->map(fn($id) => (int)$id) ->values() ->toArray(); $offerQuery = Offer::whereHas('ApplyOn', function($query) use ($sizeIds) { // 多条件OR拼接FIND_IN_SET,命中任意一个size值即满足关联条件 $query->where(function($subQuery) use ($sizeIds) { foreach ($sizeIds as $idx => $sizeId) { $sql = "FIND_IN_SET(?, size_ids)"; $idx === 0 ? $subQuery->whereRaw($sql, [$sizeId]) : $subQuery->orWhereRaw($sql, [$sizeId]); } }); }); // 追加排序逻辑:按传入size数组的顺序排序,匹配到数组中越靠前的值,记录排名越靠前 if (!empty($sizeIds)) { $placeholders = implode(',', array_fill(0, count($sizeIds), '?')); $offerQuery->join('apply_on', 'offers.id', '=', 'apply_on.offer_id') ->orderByRaw("FIELD( SUBSTRING_INDEX(SUBSTRING_INDEX(apply_on.size_ids, ',', FIND_IN_SET(?, apply_on.size_ids)), ',', -1), {$placeholders} ) ASC", array_merge([$sizeIds[0]], $sizeIds)) ->select('offers.*') ->distinct(); } $offer = $offerQuery->get();
可选排序规则调整
如果你需要按照「匹配到的size值数量越多、记录排名越靠前」的规则排序,把上述代码里的orderByRaw部分替换为如下代码即可:
$matchSql = collect($sizeIds) ->map(fn() => "IF(FIND_IN_SET(?, apply_on.size_ids), 1, 0)") ->implode('+'); $offerQuery->orderByRaw("({$matchSql}) DESC", $sizeIds);
注意事项
- 代码里的关联表名
apply_on、关联外键offer_id请和你实际的表结构、Eloquent关联配置保持一致 - 用逗号分隔字段存储多值关联属于反范式设计,
FIND_IN_SET类查询无法利用数据库索引,数据量较大时查询性能会明显下降,长期迭代建议新增多对多中间表存储关联关系,替代逗号拼接字段的存储方式
内容的提问来源于stack exchange,提问作者Akshay
相关产品推荐
相关产品推荐

