json函数查询失效或性能差的主因是路径语法错误、数据类型不匹配及缺乏索引;应优先冗余关键字段为普通列并建索引,避免在where中直接用json_contains。

直接用 JSON_EXTRACT 和 JSON_CONTAINS 查 JSON 字段,大概率查不到结果或性能崩掉——不是函数写错了,而是路径、类型、索引这三关没过。
JSON_EXTRACT 返回 NULL 的真实原因
返回 NULL 几乎从来不是因为字段为空,而是路径语法或数据类型不匹配。
-
JSON_EXTRACT路径必须以$开头;普通 key 不加引号($.name),但含空格、短横线、数字开头的 key 必须双引号包裹($."first-name"、$."123id") - 数组下标从 0 开始,且必须用方括号:
$.tags[0],写成$.tags.0或$.tags[1](越界)都返回 NULL - 字段本身是
NULL、字符串(如'{"a":1}'但列类型是VARCHAR)、或 JSON 格式非法时,JSON_EXTRACT一律返回 NULL;先用JSON_VALID(data)确认有效性 -
->是JSON_EXTRACT的简写,返回带引号的 JSON 值(如"admin");->>才等价于JSON_UNQUOTE(JSON_EXTRACT()),返回纯字符串admin;WHERE 中做等值判断务必用->>,否则'"admin"' = 'admin'永远为 false
JSON_CONTAINS 查询慢到无法接受?别在 WHERE 里直接用
JSON_CONTAINS 在大表上基本等于全表扫描,它不走索引,每次都要解析整个 JSON 字符串。哪怕只有 5000 行,响应时间也可能从几毫秒跳到秒级。
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
- 只适用于小结果集的二次过滤,比如先用普通索引查出几百条记录,再用
JSON_CONTAINS精筛 - 如果要高频按 JSON 内某个字段查(如
$.status),必须冗余为普通列(status VARCHAR(20))并建索引;或者用「存储型虚拟列 + 索引」:ALTER TABLE t ADD COLUMN status VARCHAR(20) GENERATED ALWAYS AS (data->>"$.status") STORED, ADD INDEX idx_status (status);
-
JSON_CONTAINS的candidate参数必须是合法 JSON 值:字符串要带双引号('"active"'),数字不用('1'),布尔值用'true'/'false';传'active'(无引号)会报错或返回 NULL - 不支持通配符路径(
$.*或$**),也不能用于模糊匹配;想搜子字符串,得用JSON_SEARCH,但它同样不走索引
什么时候该放弃 JSON 函数,改用普通字段
MySQL 的 JSON 支持是“能用”,不是“该用”。一旦出现以下任一情况,就该把关键字段拆出来:
- 这个字段出现在
WHERE、ORDER BY、GROUP BY或连接条件中(JOIN ON) - 单条 JSON 文档超过 10KB,或平均嵌套深度 > 3 层
- 需要对这个字段做范围查询(
BETWEEN、>)、前缀匹配(LIKE 'prefix%')或全文检索 - 业务要求强一致性校验(比如
$.price必须是正数),而 JSON 类型本身无法定义 CHECK 约束(MySQL 8.0.16+ 支持部分 JSON CHECK,但复杂度高且不通用)
最常被忽略的一点:即使你用了 JSON 类型列,只要没建虚拟列索引,所有基于内容的查询本质上都是应用层逻辑下推失败——数据库只是帮你少 parse 了一次字符串,没帮你省 CPU 或 I/O。










