MySQL 的 JSON 函数库堪称数据库界的“瑞士军刀”,功能强大但容易让人眼花缭乱。下面用一张表格 + 代码示例 + 灵魂比喻,帮你彻底搞懂这些函数!
一、函数分类速览表
| 类别 | 典型函数 | 比喻 | 核心能力 |
|---|---|---|---|
| 查询解析 | JSON_EXTRACT, ->, ->> | 数据“显微镜” | 精准提取特定字段 |
| 修改更新 | JSON_SET, JSON_REPLACE | JSON“装修队” | 局部修改不伤整体结构 |
| 创建构建 | JSON_OBJECT, JSON_ARRAY | 乐高积木组装师 | 手动构造JSON对象/数组 |
| 类型检查 | JSON_VALID, JSON_TYPE | 大家来找茬 | 验证格式合法性,识别数据类型 |
| 工具函数 | JSON_LENGTH, JSON_KEYS | 数据“体检仪” | 测量尺寸、提取键名列表 |
| 性能优化 | JSON_CONTAINS, JSON_SEARCH | 高速扫描仪 | 快速判断包含关系,定位路径 |
二、重点函数详解
1. 查询解析三剑客
-- 原始数据
SET @user = '{
"name": "李四",
"age": 28,
"address": {"city": "北京", "zip": "100000"},
"hobbies": ["coding", "篮球"]
}';
-- (1) JSON_EXTRACT(官方版)
SELECT JSON_EXTRACT(@user, '$.address.city'); -- "北京"(带引号)
-- (2) -> 操作符(语法糖)
SELECT @user->'$.hobbies[0]'; -- "coding"(带引号)
-- (3) ->> 操作符(去引号版)
SELECT @user->>'$.name' AS name; -- 李四(纯文本)
避坑指南:
- 路径表达式严格区分大小写!
$.Name和$.name是不同字段 - 数组越界返回
NULL,不会报错(像极了程序员的隐式容忍)
2. 修改更新四天王(MySQL 8.0+)
-- (1) JSON_SET:无中生有(新增字段)
SET @user = JSON_SET(@user, '$.salary', 20000);
-- 结果:添加 salary 字段
-- (2) JSON_REPLACE:偷梁换柱(修改现有字段)
SET @user = JSON_REPLACE(@user, '$.age', 29);
-- 结果:age 从28变为29
-- (3) JSON_REMOVE:断舍离(删除字段)
SET @user = JSON_REMOVE(@user, '$.hobbies[1]');
-- 结果:hobbies数组只剩 ["coding"]
-- (4) JSON_ARRAY_APPEND:数组扩容
SET @user = JSON_ARRAY_APPEND(@user, '$.hobbies', '摸鱼');
-- 结果:hobbies变成 ["coding", "摸鱼"]
性能警告:
- 修改函数会生成新JSON对象,频繁操作可能触发内存告警(类似Java的String拼接)
- 修改后的JSON大小不能超过原值!否则直接报错(防止数据膨胀)
3. 构建函数:手动造JSON
-- (1) 造对象:JSON_OBJECT(键值对自动包装)
SELECT JSON_OBJECT('name', '王五', 'age', 30);
-- 结果:{"name": "王五", "age": 30}
-- (2) 造数组:JSON_ARRAY(元素自动转JSON类型)
SELECT JSON_ARRAY(1, 'text', NOW(), JSON_OBJECT('a', 10));
-- 结果:[1, "text", "2024-02-20 12:00:00", {"a": 10}]
-- (3) 合并对象:JSON_MERGE_PATCH(8.0+覆盖式合并)
SELECT JSON_MERGE_PATCH('{"a":1}', '{"a":2, "b":3}');
-- 结果:{"a": 2, "b": 3}
冷知识:
JSON_OBJECT的键名如果是数字,会被强制转字符串(如JSON_OBJECT(1, 'test')生成{"1": "test"})
4. 类型检查与工具函数
-- (1) 合法性验证:JSON_VALID
SELECT JSON_VALID('{"name": "张三"}'); -- 1(合法)
SELECT JSON_VALID('{name: "张三"}'); -- 0(键没引号,非法)
-- (2) 类型检测:JSON_TYPE
SELECT JSON_TYPE(@user->'$.age'); -- INTEGER
SELECT JSON_TYPE(@user->'$.name'); -- STRING
SELECT JSON_TYPE(@user->'$.hobbies'); -- ARRAY
-- (3) 键名提取:JSON_KEYS
SELECT JSON_KEYS('{"a":1, "b":2}'); -- ["a", "b"]
-- (4) 长度测量:JSON_LENGTH
SELECT JSON_LENGTH('["a", "b", "c"]'); -- 3(数组长度)
SELECT JSON_LENGTH('{"a":1, "b":2}'); -- 2(对象键数量)
5. 高级搜索函数
-- (1) JSON_CONTAINS:是否包含某值
SELECT JSON_CONTAINS('[1, 2, 3]', '2', '$'); -- 1(存在)
-- (2) JSON_SEARCH:模糊查找路径
SELECT JSON_SEARCH('{"user": {"name": "李四"}}', 'one', '李四');
-- 结果:"$.user.name"(类似文件搜索的定位功能)
性能优化技巧:
- 对
JSON_CONTAINS查询建立虚拟列索引(否则全表扫描警告!) JSON_SEARCH的one/all参数控制返回第一个还是所有匹配路径
三、函数使用灵魂法则
-
路径表达式要写准:
- 键名带特殊符号用双引号:
$."user-name" - 数组索引从0开始:
$.hobbies[0]
- 键名带特殊符号用双引号:
-
类型转换要显式:
-- 错误:字符串和数字比较 SELECT * FROM products WHERE attributes->'$.price' = '100'; -- 正确:统一类型 SELECT * FROM products WHERE CAST(attributes->'$.price' AS UNSIGNED) = 100; -
索引要针对性:
-- 创建虚拟列 + 索引 ALTER TABLE products ADD COLUMN color VARCHAR(20) GENERATED ALWAYS AS (attributes->>'$.color'), ADD INDEX idx_color (color);
四、一道面试题实战
题目:如何将JSON数组 ["a","b","c"] 转换成关系型数据表?
答案:
-- 使用 JSON_TABLE 函数(MySQL 8.0+)
SELECT * FROM JSON_TABLE(
'["a", "b", "c"]',
'$[*]' COLUMNS (
id FOR ORDINALITY, -- 自动生成行号
value VARCHAR(10) PATH '$'
)
) AS t;
输出:
| id | value |
|---|---|
| 1 | a |
| 2 | b |
| 3 | c |
终极总结
- 函数选择口诀:
“查用->>,改用SET,建用OBJECT,验用VALID” - 性能口诀:
“路径要短,索引要建,类型对齐,少用通配”
名言收尾:
“不会JSON函数的DBA,就像没有扳手的修车工——面对数据只能干瞪眼。”
——《MySQL高手的自我修养》