Laravel Eloquent 查询:如何判断JSON字符串列skills与数组$skillArray是否存在交集(不使用属性转换)
解决Eloquent查询JSON字符串数组的任意匹配问题
嘿,这个场景我太熟悉了——把JSON数组存在字符串列里,普通的whereIn根本不管用,又不想用属性转换,咱们直接用数据库的JSON函数来搞定:
方法1:用MySQL 8.0.17+的JSON_OVERLAPS函数(最简洁)
如果你的MySQL版本够新,JSON_OVERLAPS是最方便的选择,它直接检查两个JSON数组是否有交集:
$skillArray = [9, 4]; // 注意:数据库里的skills是字符串元素,所以要把查询数组转成字符串数组再编码 $targetSkills = json_encode(array_map('strval', $skillArray)); $matchingEmployees = Employee::whereRaw('JSON_OVERLAPS(skills, ?)', [$targetSkills])->get();
为什么这么写?
JSON_OVERLAPS会自动解析skills列的JSON字符串,和你传入的目标JSON数组对比,只要有任意一个元素匹配就返回该行。- 必须把
$skillArray的元素转成字符串,因为数据库里存的是"1"这种字符串类型,直接传数字会因为类型不匹配导致匹配失败。
方法2:兼容低版本MySQL(用JSON_TABLE拆分数组)
如果你的MySQL版本低于8.0.17,不支持JSON_OVERLAPS,可以用JSON_TABLE把JSON数组拆成临时表,再用whereExists子查询:
$skillArray = [9, 4]; $stringSkills = array_map('strval', $skillArray); $matchingEmployees = Employee::whereExists(function ($query) use ($stringSkills) { $query->select(DB::raw(1)) // 把skills列的JSON数组拆成每行一个skill的临时表 ->from(DB::raw('JSON_TABLE(skills, "$[*]" COLUMNS(skill VARCHAR(255) PATH "$")) AS jt')) ->whereIn('jt.skill', $stringSkills); })->get();
方法3:PostgreSQL环境下的写法
如果用的是PostgreSQL,用jsonb_array_elements_text来拆分数组:
$skillArray = [9, 4]; $stringSkills = array_map('strval', $skillArray); $matchingEmployees = Employee::whereExists(function ($query) use ($stringSkills) { $query->select(DB::raw(1)) ->from(DB::raw('jsonb_array_elements_text(skills::jsonb) AS skill')) ->whereIn('skill', $stringSkills); })->get();
避坑提醒
- JSON格式有效性:确保数据库里的
skills列都是合法的JSON字符串,否则这些函数会抛出错误。 - 类型严格匹配:一定要对齐数据库里的元素类型(比如数据库存字符串,查询就传字符串),不然会出现明明有值却匹配不到的情况。
- 不推荐的REGEXP方法:如果实在没办法用JSON函数,也可以用正则,但要小心边界问题(比如
"12"会被"1"误匹配),而且要转义正则特殊字符,仅作为最后备选。
内容的提问来源于stack exchange,提问作者Akshay K Nair
相关产品推荐
相关产品推荐

