MySQL中 JSON 数据类型使用指南

一、概念篇:MySQL中的JSON

  在快速迭代的互联网时代,数据已成为驱动业务增长和创新的核心要素。然而,随着应用场景的多样化和复杂化,传统固定表结构常常成为开发者的噩梦。正是在这样的背景下,MySQL 作为世界上最流行的开源关系型数据库之一,从 5.7 版本开始正式引入了原生 JSON 数据类型,标志着关系型数据库在处理非结构化数据方面迈出了重要一步,为关系型数据库中存储、查询和操作这类数据提供了强有力的工具。

  从 MySQL 5.7 版本引入了JSON数据类型开始,JSON 格式字符串将不再以字符串的形式存储,而是采用一种允许快速读取文本元素的内部二进制格式。本质上,MySQL 的 JSON 数据类型扮演了"桥梁"角色:既保留了关系型数据库的强大事务处理能力和成熟生态系统,又吸收了非结构化数据处理的高度灵活性。在 MySQL 中使用 JSON 数据类型,可以简化半结构化或层级数据的处理流程,带来诸多优势:

  • 灵活性:JSON 结构支持动态、层级数据的灵活存储。
  • 内置函数:MySQL 提供高效的 JSON 数据查询、更新和验证函数。
  • 集成性:可以将关系型数据与 JSON 对象结合,实现混合数据模型。
特性 MySQL 5.7 MySQL 8.0
JSON数据类型 ✅ 基础支持 ✅ 完整支持
JSON函数 ✅ 基础函数 ✅ 丰富函数库
JSON索引 ✅ 函数索引 ✅ 多列索引
JSON性能 ⚠️ 一般 ✅ 大幅提升
JSON验证 ✅ 基础验证 ✅ 严格验证

二、MySQL JSON数据类型基础

2.1 创建 JSON 数据

  在MySQL中声明JSON列非常简单,只需在创建表或修改表结构时指定列的数据类型为JSON即可。以下是一个基本的建表示例:

sql 复制代码
CREATE TABLE users (
	id INT PRIMARY KEY auto_increment,
	user_id INT NOT NULL UNIQUE,
	NAME VARCHAR ( 32 ),
	details JSON NOT NULL COMMENT '用户详情信息',
	created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;

  在上述示例中,details 列被定义为JSON类型,用于存储用户的配置信息或其他非结构化数据。需要注意的是,JSON 列不能有默认值,且不支持直接索引。

2.2 插入 JSON 数据

  MySQL 的 JSON 类型遵循 JSON 标准,这一标准明确规定了 JSON 数据的有效结构形式。向 JSON 列插入数据时,必须确保数据是有效的 JSON 格式,在插入时MySQL会自动进行验证,未通过验证的文本将产生一个错误信息,从而阻止数据的正常插入。以下是几种插入JSON数据的常见方式:

  • 直接插入JSON字符串

    sql 复制代码
    INSERT INTO users (user_id,NAME,details) VALUES (1001,'张三','{
        "age": 30,
        "sex": 1,
        "email": "zhangsan@example.com",
    		"hobbies": ["coding", "reading"],
        "address": {
            "city": "北京",
            "zip": "10001"
        },
        "role": [
            {
                "roleCode": "guest",
                "roleName": "访客人员"
            },
            {
                "roleCode": "tester",
                "roleName": "测试人员"
            }
        ]
    }
    ');
    
    INSERT INTO users (user_id,NAME,details) VALUES (1002,'李四','{
        "age": 25,
        "sex": 2,
        "email": "lisi@example.com",
    		"hobbies": ["coding", "reading"],
        "address": {
            "city": "上海",
            "zip": "90001"
        },
        "role": [
            {
                "roleCode": "system",
                "roleName": "系统管理员"
            },
            {
                "roleCode": "developer",
                "roleName": "系统开发人员"
            }
        ]
    	}
    ');
  • 使用JSON函数生成JSON数据 :MySQL提供如下 JSON 函数来动态构建 JSON 数据,这种方式可以避免手动拼接JSON字符串时可能出现的格式错误

    函数 简要说明
    JSON_OBJECT() 将键值对转换为 JSON 对象,键值对自动包装
    JSON_ARRAY() 将一组值转换为 JSON 数组,元素自动转JSON类型
    sql 复制代码
    -- 构造一个 JSON 对象
    SELECT JSON_OBJECT('name', 'John Doe', 'age', 30) AS person;
    
    -- 构造一个 JSON 数组
    SELECT JSON_ARRAY('apple', 'banana', 'cherry') AS fruits;

    ⚠️ JSON_OBJECT 的键名如果是数字,会被强制转字符串(如 JSON_OBJECT(1, 'test') 生成 {"1": "test"}

2.3 查询 JSON 数据

  MySQL 对JSON类型字段提供许多内置函数,可以灵活的处理JSON数据。常见操作包括:提取 JSON 字段值(通过键或路径查询)、检索嵌套字段值、过滤和排序 JSON 数据等。

提取 JSON 字段值

  JSON_EXTRACT() 是提取字段值的核心函数,用来从 JSON 数据字段中获取指定路径的值。JSON_EXTRACT() 函数的语法格式如下:

sql 复制代码
JSON_EXTRACT(json_doc, path[, path] ...)

其中,json_doc 是 JSON 文档,path 是路径。该函数会从 JSON 文档提取指定路径(path)的元素。如果指定 path 不存在,会返回 NULL。可指定多个 path,匹配到的多个值会以数组形式返回。

sql 复制代码
-- 提取 JSON 字段中的 "email"
SELECT JSON_EXTRACT(details, '$.email') AS email FROM users;

-- 提取JSON字段的"email"和"age"字段,结果以数组形式返回
SELECT JSON_EXTRACT(details,'$.email','$.age')  FROM users;

-- 获取JSON数组的SQL
SELECT JSON_EXTRACT(details,'$.hobbies[0]') AS name FROM users;

  如果 JSON 数据包含嵌套结构,可以通过路径表达式逐层访问。

sql 复制代码
-- 查询嵌套字段 "details.location.city"
SELECT JSON_EXTRACT(details, '$.address.city') AS city FROM users;

过滤和排序 JSON 数据

  通过 WHERE 子句和路径表达式筛选满足条件的数据。

sql 复制代码
-- 查询 JSON 数据中 "age" 大于 25 的记录
SELECT * FROM users WHERE JSON_EXTRACT(details, '$.age') > 25;

-- 查询 JSON 数据中 "email" 为 "zhangsan@example.com" 的记录
SELECT * FROM users WHERE JSON_UNQUOTE(JSON_EXTRACT(details, '$.email')) = 'zhangsan@example.com';

⚠️ JSON_UNQUOTE() 用于移除 JSON_EXTRACT() 返回值中的双引号,提取字段的原始值。

2.4 修改 JSON 数据

sql 复制代码
-- 更新张三的城市为shandong
UPDATE users SET details = JSON_SET(details, '$.address.city', '山东') WHERE name = '张三';

-- 删除张三的地址信息
UPDATE users SET details = JSON_REMOVE(details, '$.address') WHERE name = '张三';

三、JSON数据操作:查询与更新技巧

  MySQL 的 JSON 函数库堪称数据库界的"瑞士军刀",可以快速对JSON类型的数据进行查询、更新,这也是 MySQ L中处理 JSON 数据最频繁的操作。这些函数不仅简化了操作流程,还显著提升了数据处理的灵活性和性能。

3.1 查询解析

  这是日常开发中最常用的功能,数据被转换为 JSON 格式后,即可对其进行快速解析,高效提取数据。

函数 简要说明
json_extract(json_doc, path) 标准提取函数,用于从 JSON 文档中提取指定路径的值
JSON_UNQUOTE 提取字段值并去除引号
sql 复制代码
-- 原始数据
SET @user = '{
  "age": 30,
  "sex": 1,
  "email": "zhangsan@example.com",
  "hobbies": ["coding", "reading"],
  "address": {
    "city": "北京",
    "zip": "10001"
  },
  "role": [
    {
      "roleCode": "guest",
      "roleName": "访客人员"
    },
    {
      "roleCode": "tester",
      "roleName": "测试人员"
    }
  ]
}';

-- 从JSON对象中取出单个值(带引号)
SELECT JSON_EXTRACT(@user, '$.address.city');
SELECT JSON_EXTRACT(@user, '$.email');

-- 从JSON数组取出单个值
SELECT JSON_EXTRACT(@user, '$.hobbies[0]');
SELECT JSON_EXTRACT(@user, '$.role[0]');

  除了标准的提取函数,MySQL 提供了基于操作符的语法糖操作:

函数 简要说明
-> 用于提取带有引号的字符串或其他合法的 JSON 数据类型
->> 用于提取 JSON 字段并返回原始值(移除了 JSON 格式中的双引号)
sql 复制代码
-- 提取名字(带双引号)
SELECT name, details->'$.email' FROM users;

-- 提取名字(纯文本,推荐)
SELECT name, details->>'$.email' FROM users;

⚠️ 注意:路径表达式严格区分大小写(如 $.Name$.name 是不同的);如果数组越界或路径不存在,会返回 NULL

3.2 修改与更新

  以下函数用于更新或添加 JSON 数据元素:

函数 简要说明
json_insert(json_doc, path, val, path, val ...) 仅新增 ,只在路径不存在时插入值,若路径已存在则不做任何操作
json_set(json_doc, path, val, ...) 用于插入或更新JSON文档中的值。如果指定路径不存在则新增;如果存在则覆盖修改
json_replace(json_doc, path, val, path, val ...) 仅修改 。只在路径已存在时替换值,若路径不存在则不做任何操作
json_remove(json_doc, path, path ...) 用于从JSON文档中删除指定路径的数据。 如果路径存在则删除,如果路径不存在,则返回原 JSON 数据
sql 复制代码
-- 原始数据
SET @user = '{
    "id": 14,
    "name": "张三",
    "age": 30
}';

-- 仅在 email 不存在时添加
SELECT json_insert(@user, '$.email', 'test@example.com');

-- 存在则更新字段
SELECT JSON_SET(@user, '$.age', 35);
-- 不存在则新增字段
SELECT JSON_SET(@user, '$.gender', 'male');

-- 如果目标路径中已经存在值,那么会将其替换为新的值
SELECT json_replace(@user, '$.age', 35);
SELECT json_replace(@user, '$.gender', 'male');

-- 对于JSON对象,只需指定要删除的键即可
SELECT json_remove(@user, '$.age');

⚠️ 修改函数会生成新 JSON 对象(类似 Java 的 String 拼接),频繁操作可能触发内存告警。

3.3 类型检查与验证

函数 简要说明
JSON_TYPE 获取 JSON 值的数据类型。 除了是MySQL中的数据类型外,还有OBJECT、ARRAY,其中OBJECT表示JSON对象,ARRAY表示JSON数组
JSON_VALID 判断一个字符串是否是合法的 JSON 格式,常用于数据清洗 如果是,则返回1,否则返回0,如果value的值为NULL,则返回NULL
sql 复制代码
-- 原始数据
SET @user = '{
    "id": 14,
    "name": "张三",
    "age": 30,
    "address": {"city": "北京", "zip": "100000"},
    "hobbies": ["coding", "篮球"]
}';

SELECT JSON_TYPE(@user);

SELECT JSON_TYPE(JSON_EXTRACT(@user,'$.age'));    -- INTEGER
SELECT JSON_TYPE(JSON_EXTRACT(@user,'$.name'));  -- STRING
SELECT JSON_TYPE(JSON_EXTRACT(@user,'$.hobbies')); -- ARRAY

SELECT JSON_VALID('{"name": "test"}');
SELECT JSON_VALID('{name: test}');
SELECT JSON_VALID('null'), JSON_VALID('Null'), JSON_VALID('NULL');

3.4 搜索与判断

  用于在复杂的 JSON 结构中查找数据或判断包含关系:

函数 简要说明
json_contains(json_doc, target, path) 用于检查JSON文档中是否包含某个值,如果包含则返回1,否则返回0
json_contains_path(json_doc, one_or_all, path ,path ...) 判断 JSON 文档是否包含指定的路径
json_search(json_doc, one_or_all, search_str, path) 查找指定字符串在 JSON 中的路径。 one 表示找到第一个就返回,all 表示返回所有匹配的路径。
sql 复制代码
-- 判断 hobbies 中是否包含 "coding"(注意值需要用 JSON 字符串格式 '"coding"')
SELECT json_contains('{"name":"李四","hobbies":["coding","reading"]}', '"coding"', '$.hobbies');

-- 搜索值为 "张三" 的路径(返回第一个匹配)
SELECT json_search('{"name": "张三", "age": 25, "city": "北京"}', 'one', '张三');
-- 搜索包含 "海" 的值(使用通配符)
SELECT json_search('[{"name":"张三","age":25,"city":"北京"},{"name":"李四","age":30,"city":"上海"}]', 'one', '%海%')
-- 搜索所有包含 "张" 的值
SELECT json_search('[{"name":"张三","age":25,"city":"北京"},{"name":"李四","age":30,"city":"上海"}]', 'all', '%张%')
-- 指定搜索路径:只在 name 字段中搜索
SELECT json_search('{"name": "张三", "age": 25, "city": "北京"}', 'one', '%三%', NULL, '$.name')

⚠️ JSON_SEARCH 只匹配字符串类型,不匹配数字、布尔值等。

四、提升JSON数据处理效率

4.1 数据合并

函数 简要说明
JSON_MERGE 用于合并两个JSON文档。在 MySQL 8.0 中,已不建议使用
JSON_MERGE_PRESERVE 合并 JSON 值,保留重复键的值
JSON_MERGE_PATCH 合并 JSON 值,丢弃除最后一个值以外的所有值

json_merge_preserve() 函数

  json_merge_preserve() 在合并 JSON 数组时,会把所有元素简单的拼接成一个大数组:

sql 复制代码
-- 输出结果:[1, 2, true, false, "a", "b"]
SELECT json_merge_preserve('[1, 2]', '[true, false]','["a","b"]');
-- 输出结果:[1, 2, {"key1": "value1"}]
SELECT json_merge_preserve('[1, 2]', '{"key1": "value1"}');

  如果合并的是 JSON 对象,json_merge_preserve()函数会保留所有非重复键值对,对于重复的键,会将其值合并为JSON数组保留下来

sql 复制代码
-- 输出结果:{"key1": "value1", "key2": "value2"}
SELECT json_merge_preserve('{"key1": "value1"}','{"key2": "value2"}');

-- 输出结果:{"key1": ["value1", "value3"], "key2": "value2"}
SELECT json_merge_preserve('{"key1": "value1"}','{"key2": "value2","key1":"value3"}') duplicate_key1;

-- 输出结果:{"key1": ["value1", "value3", null], "key2": "value2"}
SELECT json_merge_preserve('{"key1": "value1"}','{"key2": "value2","key1":"value3"}','{"key1": null}') last_value_is_null;

json_merge_patch() 函数

  json_merge_patch() 在合并JSON数组时,会将每个参数视为独立的元素,并应用"最后的值胜出"原则,只选择最后一个元素,因此只要函数中包含JSON数组,只有最后一个值会保留下来:

sql 复制代码
-- 输出结果:["a", "b"]
SELECT json_merge_patch('[1, 2]', '[true, false]','["a","b"]');
-- 输出结果:{"key1": "value1"}
SELECT json_merge_patch('[1, 2]', '{"key1": "value1"}');
-- 输出结果:[1, 2]
SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2"}','[1, 2]');

  如果合并的都是JSON对象, json_merge_patch()函数会保留所有非重复键值对,对于重复的键,只保留最后出现的值(但最后出现的值不能为null,否则会删除该键值):

sql 复制代码
-- 输出结果:{"key1": "value1", "key2": "value2"}
SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2"}');
-- 输出结果:{"key1": "value3", "key2": "value2"}
SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2","key1":"value3"}') duplicate_key;
-- 输出结果:{"key2": "value2"}
SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2","key1":"value3"}','{"key1": null}') last_value_is_null;

4.2 JSON 数据聚合

  MySQL 提供多种函数用于将查询结果聚合为 JSON 结构:

函数 简要说明
JSON_ARRAYAGG()
JSON_OBJECTAGG()
sql 复制代码
SELECT JSON_ARRAYAGG(name) AS names 
FROM ( SELECT JSON_EXTRACT(details, '$.name') AS name FROM users) AS subquery;

4.3 JSON 数组中的范围

  数组类型的JSON,可以使用带有 to 关键字的 range 来指定 JSON 数组的子集。比如可以通过 [M to N] 获取数组的子集,通过 [*] 获取数组中的所有元素。

sql 复制代码
-- 获取数组的第二、第三和第四个元素
SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[1 to 3]');

SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[*]');

⚠️ 注意的是,虽然 JSON 类型同时支持 JSON 对象和 JSON 数组两种类型,但在使用的时候最好不要两者混用,否者在上述两种查询种会出现一些不符合预期的结果。

4.4 最右边的数组元素

  支持用 last 关键字作为数组中最后一个元素下标的同义词。last-N 形式的表达式可用于相对地址和范围定义,如下所示:

sql 复制代码
SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[last-3 to last-1]');

4.5 JSON数据与索引

  在 MySQL 的表中,JSON 类型的列通常无法直接建立索引,但可以用虚拟生成列,并根据该列来建立间接索引。但是在 MySQL 8 版本开始,对于 JSON 数组(JSON对象不行),可以建立多值索引。

索引优化方案

  对于JSON数据类型需要建立索引,可以对将经常查询的元素提取出来,作为一个虚拟的生成列,并在该列上建立索引,查询时通过虚拟列上索引即可快速定位数据。例如,为用户表中的邮箱创建虚拟列和索引。

sql 复制代码
ALTER TABLE users ADD COLUMN email VARCHAR(255) GENERATED ALWAYS AS (details->>'$.email') STORED;
CREATE INDEX idx_email ON users (email);

使用全文索引

  对于包含大量文本的JSON字段,可以使用全文索引来提高查询性能。

sql 复制代码
ALTER TABLE users ADD FULLTEXT(details);
SELECT * FROM users WHERE MATCH(details) AGAINST('shanghai');

六、总结

  通过本文的介绍,我们了解了MySQL中JSON数据类型的基本操作、常用JSON函数、以及如何通过索引和优化来提高查询性能。JSON数据类型为存储和操作结构化数据提供了灵活性和便利性,在现代数据库应用中具有广泛的应用前景。

相关推荐
ejinxian1 小时前
PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等数据库
数据库·mysql·mongodb
大白要努力!8 小时前
MySQL 8.0 + Navicat 完整操作指南
数据库·mysql
云絮.9 小时前
数据库操作
数据库·mysql·算法·oracle
设计师小聂!11 小时前
宝塔 Linux 面板保姆级教程
linux·mysql·开源·运维开发
Tong Z12 小时前
Mysql DDL中的ALGORITHM
数据库·mysql
minji...16 小时前
MySQL数据库 (七) MySQL表的基本查询(上),insert、replace、select、where、order by
数据库·mysql·select·replace·insert·order by·where
折戟不必沉沙18 小时前
mysql忘记密码
数据库·mysql
kuonyuma18 小时前
MyBatis入门·注解操作
java·spring boot·mysql·spring·mybatis
聪明努力的积极向上18 小时前
【claude code】MySQL MCP 配置完整指南
数据库·mysql·ai编程
DIY源码阁18 小时前
JavaSwing酒店管理系统 - MySQL版
java·mysql·eclipse