MySQL 5.7+ 中 whereJsonContains 用于查询 JSON 字段是否包含指定值,要求字段类型为 JSON、路径语法正确(如 'options->tags')、数据为合法 JSON 格式,且不走索引需配合生成列优化性能。
MySQL 5.7+ 直接查 JSON 字段的 whereJsonContains 怎么用
只要数据库是 MySQL 5.7 或更高版本,Eloquent 原生支持
、
、
这类方法,不用手写 raw 查询。它底层调用的是 MySQL 的
函数,要求字段类型是
(不是
或
)。
常见错误是字段用了
类型却硬套 JSON 查询——会静默失败或返回空结果,因为 MySQL 不识别其为合法 JSON。
:查
字段里
数组是否包含字符串
(注意引号,值必须是完整 JSON 值)
:查
数组是否同时包含这两个字符串(顺序无关)
如果字段名带点(如
),不能直接写
,得用
或改用
Laravel 9+ 的 whereJsonContains 不生效?检查这三点
不是语法写错,而是环境或数据结构不匹配。最常踩的坑是 JSON 数据本身格式不对。
数据库存的是字符串
(转义双引号),而不是真正的 JSON 数组
——
会返回空,因为 MySQL 拒绝解析非法 JSON
Eloquent 模型没加
,导致存入时没走 JSON 编码,实际存的是序列化字符串(
),这种根本不是 JSON
查询路径用了单引号:
,PHP 解析后变成
,MySQL 报错
;必须统一用双引号或不加引号(如
)
PostgreSQL 怎么查 JSONB 字段?别硬套 MySQL 写法
PostgreSQL 用的是
类型和
操作符,Eloquent 的
在 PG 下会自动编译成
,但前提是字段定义为
,且数据是标准 JSONB 格式。
立即学习
“
PHP免费学习笔记(深入)
”;
如果你手动写了
,要注意:传入的必须是 JSON 字符串字面量,不是 PHP 数组。Laravel 自动处理了这个转换,所以优先用 Eloquent 方法而非 raw。
查嵌套对象:
在 PG 和 MySQL 都可用,但 PG 要求
和
都存在,否则报错;MySQL 更宽松,路径不存在时返回 false
想查 key 是否存在(不管值),用
(PG)或
(MySQL)
PG 对大小写敏感,
≠
;MySQL 默认也敏感,除非字段用 utf8mb4_bin 排序规则
复杂 JSON 查询性能差?加生成列 + 索引才是正解
单纯在 JSON 字段上建索引(比如
)对 MySQL 5.7+ 没用,JSON 类型不支持普通 B-Tree 索引。必须用生成列(generated column)把要查的路径“抽出来”,再给它加索引。
例如经常按
查询,就在迁移里加:
之后就可以用
,速度接近普通字段查询。别指望
走索引——它永远全表扫描,只是语法方便而已。
PostgreSQL 可以直接在
json
b 上建 GIN 索引:
,但只加速
、
这类操作,对
提取后比较仍需配合表达式索引。
whereJsonContainswhereJsonLengthwhereJsonDoesntContainJSON_CONTAINS()jsontextlongtexttextwhereJsonContains('options->tags', 'vue')optionstags"vue"whereJsonContains('meta->permissions', ['read', 'write'])permissionsuser.profileuser.profile->nameuser->profile->namewhereRaw"[\"vue\",\"laravel\"]"["vue","laravel"]whereJsonContains$casts = ['options' => 'array']a:2:{i:0;s:4:"vue";i:1;s:8:"laravel";}'options->'tags''options->'tags'Invalid JSON path expressionoptions->tagsjsonb@>whereJsonContainsjsonb @> ?jsonbwhereRaw("meta @> ?", ['{"role": "admin"}'])whereJsonContains('meta->settings->theme', 'dark')settingsthemewhereRaw("meta ? 'role'")whereRaw("JSON_EXTRACT(meta, '$.role') IS NOT NULL")'Admin''admin'INDEX(options)options->$.statusDB::statement("ALTER TABLE users ADD COLUMN options_status VARCHAR(20) AS (JSON_UNQUOTE(JSON_EXTRACT(options, '$.status'))) STORED");
DB::statement("CREATE INDEX idx_users_options_status ON users(options_status)");where('options_status', 'active')whereJsonContainsCREATE INDEX idx_meta_gin ON users USING GIN (meta)@>?->>