Laravel 查询 JSON 列:高效筛选包含数组中任意值的记录
技术百科
心靈之曲
发布时间:2026-01-01
浏览: 次 本文详解如何在 laravel 8 中使用 query builder 对 json 数组列(如 `location_ids`)进行高效过滤,支持“至少匹配一个”语义,并提供 `wherejsoncontains` 循环方案与 mysql 8.0.17+ 推荐的 `json_overlaps` 原生函数两种实现方式。
在 Laravel 应用中,当关联表的 JSON 列(例如 location_ids)存储字符串化整数数组(如 ["1", "2", "5"]),而我们需要筛选出该 JSON 数组中至少包含 $locations(整型数组,如 [1, 3, 7])中任一元素的记录时,不能直接传入数组调用 whereJsonContains —— 因为该方法默认将整个参数作为单个值进行子串/元素匹配,而非集合交集判断。
✅ 推荐方案一:使用 JSON_OVERLAPS(MySQL ≥ 8.0.17)
若数据库版本支持(推荐生产环境优先采用),JSON_OVERLAPS 是最简洁、高效且语义清晰的原生方案。它直接判断两个 JSON 文档是否存在公共元素(
自动处理类型转换与嵌套数组):
public function scopeViewable($query)
{
$user = Auth::user();
$locations = $user->getShopAccess()->pluck('id')->values(); // 确保索引连续,避免 toJson() 产生关联数组
if ($user->hasPermissionTo('users.index')) {
return $query->whereHas('shopAccess', function (Builder $q) use ($locations) {
$q->whereRaw('JSON_OVERLAPS(location_ids, ?)', [$locations->toJson()]);
});
}
return $query->whereNull('id'); // 或其他默认限制逻辑
}⚠️ 注意事项:
- pluck('id')->values() 确保生成标准数值索引数组(如 [0 => 1, 1 => 3]),否则 toJson() 可能输出 {"0":"1","1":"3"}(对象格式),导致 JSON_OVERLAPS 匹配失败;
- location_ids 列值必须为合法 JSON 数组(如 ["1","2","5"]),且数据库字符集需为 utf8mb4;
- 此方法在 MySQL 层完成计算,性能远优于 PHP 循环 + 多次 OR,且可利用生成列 + 索引进一步优化(如添加 JSON_CONTAINS 虚拟列索引)。
✅ 方案二:兼容低版本 MySQL 的 whereJsonContains 循环
若数据库版本低于 8.0.17,可退而求其次,对 $locations 数组逐项调用 whereJsonContains 并组合为 OR 条件:
public function scopeViewable($query)
{
$user = Auth::user();
$locations = $user->getShopAccess()->pluck('id')->toArray();
if ($user->hasPermissionTo('users.index')) {
return $query->whereHas('shopAccess', function (Builder $q) use ($locations) {
$q->where(function ($sub) use ($locations) {
foreach ($locations as $location) {
// 将整数转为字符串,匹配 JSON 中的 "1"、"2" 等
$sub->orWhereJsonContains('location_ids', (string) $location);
}
});
});
}
return $query->whereNull('id');
}? 关键细节:
- whereJsonContains('location_ids', '1') 能正确匹配 ["1","2","5"],但不能匹配 ["10","11"](精确元素匹配,非子串);
- 必须将整型 $location 显式转为字符串 (string) $location,否则 Laravel 可能传递整数 1,而 JSON 中存储的是字符串 "1",导致匹配失败;
- 当 $locations 较大时(如 > 100 项),生成的 SQL OR 链过长可能影响查询计划,建议配合缓存或预聚合优化。
? 总结与选型建议
| 方案 | 适用场景 | 性能 | 可维护性 | 备注 |
|---|---|---|---|---|
| JSON_OVERLAPS | MySQL ≥ 8.0.17 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 推荐首选;一行代码,语义明确,数据库原生优化 |
| orWhereJsonContains 循环 | 兼容旧版 MySQL | ⭐⭐☆ | ⭐⭐⭐ | 注意字符串转换与数组结构;适合中小规模数据 |
无论采用哪种方式,请务必在 shop_access 表的 location_ids 列上建立生成列(Generated Column)并添加索引以提升查询性能,例如:
ALTER TABLE shop_access ADD COLUMN location_ids_json JSON AS (CAST(location_ids AS JSON)) STORED, ADD INDEX idx_location_overlap (location_ids_json);
然后在查询中改用 JSON_OVERLAPS(location_ids_json, ?) 进一步加速。
# ai
# 的是
# 两种
# 而非
# 哪种
# 或其他
# 可利用
# 旧版
# js
# json
# 循环
# 对象
# String
# 字符串
# 数据库
# access
# red
# php
# 类型转换
# mysql
# sql
# 整型
# 是否存在
# location
# laravel
# column
# 退而求其次
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11麦克风没声音怎么设置_Win11麦克风权
- Windows服务持续崩溃怎样修复_系统服务保护机
- 使用类变量定义字符串常量时如何实现类型安全的 Li
- 如何使用 Python 合并文件夹内多个 Exce
- Windows11如何设置专注助手_Windows
- mac怎么安装adb_MAC配置Android A
- C++如何使用std::transform批量处理
- c# 如何用c#实现一个支持优先级的任务队列
- Win11怎么解压RAR文件 Win11自带解压功
- php订单日志怎么记录物流_php记录订单物流变更
- 如何使用Golang实现容器自动化运维_Golan
- Win11时间怎么同步到原子钟 Win11高精度时
- 如何在Golang中实现WebSocket广播_使
- 如何正确访问 Laravel 模型或对象的属性而非
- XML的“混合内容”是什么 怎么用DTD或XSD定
- mac怎么分屏_MAC双屏显示与分屏操作技巧【指南
- Win11如何设置环境变量 Win11添加和修改系
- php在Linux怎么部署_LNMP环境搭建PHP
- Win11怎么设置声音输出设备_Windows11
- mac怎么安装字体_MAC添加第三方字体与字体册管
- Win11怎么设置指纹解锁 Win11笔记本录入指
- Win11怎么快速锁屏_Win11一键锁屏快捷键W
- Mac怎么进行语音输入_Mac听写功能设置与使用【
- Python技术债务管理_长期维护解析【教程】
- Win11怎么清理C盘系统错误报告_Win11清理
- php485能和物联网模块通信吗_php485对接
- Win11如何设置系统声音_Win11系统声音调整
- Win11鼠标灵敏度怎么调 Win11鼠标指针移动
- Win10怎样卸载TeamViewer_Win10
- 如何在Golang中处理模块冲突_解决依赖版本不兼
- Win11开机Logo怎么换_Win11自定义启动
- 如何使用Golang benchmark测量函数延
- 如何使用Golang安装依赖库_管理模块和第三方包
- Windows10系统怎么查看系统版本_Win10
- Win11如何设置自动关机 Win11定时关机命令
- Win11怎么关闭VBS安全性_Windows11
- Windows10任务栏图标变成白色文件_Win1
- Windows10怎么查看系统激活状态_Windo
- Win11怎么设置屏保时间_调整Win11屏幕保护
- Mac如何将HEIC图片格式转为JPG_Mac批量
- Win10电脑怎么设置网络名称_Windows10
- 如何处理“XML格式不正确”错误 常见XML we
- Win10如何关闭安全中心所有通知 Win10禁用
- PythonPandas数据分析教程_数据清洗与处
- 如何在 Go 结构体中正确初始化 map 字段
- Win11怎么设置多显示器任务栏 Win11扩展任
- 如何在Golang中使用encoding/gob序
- Win11怎么关闭触摸键盘图标_Windows11
- Windows怎样关闭开始菜单广告_Windows
- Windows10如何更改桌面背景_Win10个性

QQ客服