MySQL/Laravel实现反向FIND_IN_SET:匹配字符串包含的分隔值
实现MySQL逗号分隔列的反向匹配(与FIND_IN_SET逻辑相反)
MySQL原生实现
要实现「输入字符串包含列中某一个逗号分隔值」的匹配,核心是先将逗号分隔的列拆分为单个值,再逐一检查是否被输入字符串包含。优先推荐用递归CTE(Common Table Expression)实现,兼容性和准确性更好。
假设你的表名为your_table,存储逗号分隔值的列名为target_column,输入字符串为'The Dog Jumps High',示例SQL如下:
SET @input_str = 'The Dog Jumps High'; WITH RECURSIVE split_values AS ( -- 初始化:提取每条记录的第一个分隔值 SELECT id, target_column, SUBSTRING_INDEX(target_column, ',', 1) AS single_value, SUBSTRING(target_column, LENGTH(SUBSTRING_INDEX(target_column, ',', 1)) + 2) AS remaining_values FROM your_table WHERE target_column IS NOT NULL AND target_column != '' UNION ALL -- 递归拆分剩余的分隔值 SELECT id, target_column, SUBSTRING_INDEX(remaining_values, ',', 1) AS single_value, SUBSTRING(remaining_values, LENGTH(SUBSTRING_INDEX(remaining_values, ',', 1)) + 2) AS remaining_values FROM split_values WHERE remaining_values IS NOT NULL AND remaining_values != '' ) -- 筛选存在符合条件的单个值的记录,去重避免重复返回 SELECT DISTINCT t.* FROM your_table t JOIN split_values sv ON t.id = sv.id WHERE LOCATE(sv.single_value, @input_str) > 0;
补充说明
LOCATE函数是大小写敏感的,若需忽略大小写,可改为LOCATE(LOWER(sv.single_value), LOWER(@input_str)) > 0;- 如果你的MySQL版本低于8.0(不支持CTE),可以用正则匹配的替代方案(仅适合输入无特殊正则字符的场景):
SET @input_str = 'The Dog Jumps High'; SELECT * FROM your_table WHERE target_column REGEXP CONCAT('(^|,)', REPLACE(REGEXP_REPLACE(@input_str, '[^a-zA-Z0-9 ]', ''), ' ', '|'), '(,|$)');
Laravel Eloquent实现
方式1:递归CTE方案(推荐,Laravel 8+支持)
通过DB::withExpression定义递归CTE,结合Eloquent查询构造器实现:
use Illuminate\Support\Facades\DB; use App\Models\YourModel; $inputStr = 'The Dog Jumps High'; $results = YourModel::query() ->withExpression('split_values', function ($query) { $query->select('id', 'target_column') ->selectRaw("SUBSTRING_INDEX(target_column, ',', 1) AS single_value") ->selectRaw("SUBSTRING(target_column, LENGTH(SUBSTRING_INDEX(target_column, ',', 1)) + 2) AS remaining_values") ->from('your_table') ->whereNotNull('target_column') ->where('target_column', '!=', '') ->unionAll(function ($query) { $query->select('id', 'target_column') ->selectRaw("SUBSTRING_INDEX(remaining_values, ',', 1) AS single_value") ->selectRaw("SUBSTRING(remaining_values, LENGTH(SUBSTRING_INDEX(remaining_values, ',', 1)) + 2) AS remaining_values") ->from('split_values') ->whereNotNull('remaining_values') ->where('remaining_values', '!=', ''); }); }) ->join('split_values', 'your_table.id', '=', 'split_values.id') ->whereRaw("LOCATE(split_values.single_value, ?) > 0", [$inputStr]) ->distinct() ->get();
方式2:简化正则匹配(适合简单场景)
如果输入字符串不含特殊正则字符,也可以直接用whereRaw快速实现:
use App\Models\YourModel; $inputStr = 'The Dog Jumps High'; // 转义正则特殊字符,避免匹配异常 $escapedInput = preg_quote($inputStr, '/'); // 替换空格为|,匹配输入中包含的任意片段 $pattern = "(^|,)" . str_replace(' ', '|', $escapedInput) . "(,|$)"; $results = YourModel::query() ->whereRaw("target_column REGEXP ?", [$pattern]) ->get();
补充说明
- 若需忽略大小写,可在
whereRaw中修改为LOCATE(LOWER(split_values.single_value), LOWER(?)) > 0; - 请将代码中的
your_table、target_column、YourModel替换为实际业务对应的表名、字段名和模型类。
内容的提问来源于stack exchange,提问作者sony1976
相关产品推荐
相关产品推荐

