MySQL 的 JSON 函数库

337 阅读2分钟

MySQL 的 JSON 函数库堪称数据库界的“瑞士军刀”,功能强大但容易让人眼花缭乱。下面用一张表格 + 代码示例 + 灵魂比喻,帮你彻底搞懂这些函数!


一、函数分类速览表

类别典型函数比喻核心能力
查询解析JSON_EXTRACT, ->, ->>数据“显微镜”精准提取特定字段
修改更新JSON_SET, JSON_REPLACEJSON“装修队”局部修改不伤整体结构
创建构建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_SEARCHone/all 参数控制返回第一个还是所有匹配路径

三、函数使用灵魂法则

  1. 路径表达式要写准

    • 键名带特殊符号用双引号:$."user-name"
    • 数组索引从0开始:$.hobbies[0]
  2. 类型转换要显式

    -- 错误:字符串和数字比较
    SELECT * FROM products WHERE attributes->'$.price' = '100'; 
    
    -- 正确:统一类型
    SELECT * FROM products 
    WHERE CAST(attributes->'$.price' AS UNSIGNED) = 100;
    
  3. 索引要针对性

    -- 创建虚拟列 + 索引
    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;

输出

idvalue
1a
2b
3c

终极总结

  • 函数选择口诀
    “查用->>,改用SET,建用OBJECT,验用VALID
  • 性能口诀
    “路径要短,索引要建,类型对齐,少用通配”

名言收尾
“不会JSON函数的DBA,就像没有扳手的修车工——面对数据只能干瞪眼。”
——《MySQL高手的自我修养》