【MySQL】MySQL `JSON` 数据类型介绍
自 MySQL 5.7 引入原生 JSON
数据类型以来,它已成为处理半结构化数据的利器。本指南将全面覆盖 JSON
类型的写入、读取、更新、删除、高级查询及性能优化。
1. 核心优势与适用场景
- 三大优势:
- 自动格式验证: 保证存入的数据一定是有效的JSON。
- 优化二进制存储: 读取JSON内部元素时比文本解析更快。
- 丰富的内置函数: 提供强大的查询和操作能力。
- 适用场景:
- 商品规格、文章标签、用户配置等多变属性的存储。
- 不适用场景: 用户ID、用户名等高度结构化、需要频繁精确查询的核心数据,仍应使用传统字段。
2.【写入】构造与插入 JSON 数据
这是JSON
类型最基础的操作:如何创建并写入数据。
A. 插入预先格式化的JSON字符串(基本方式)
这是最常见的方式,在你的应用程序代码中(如Python, Java)将对象序列化为JSON字符串,然后直接插入。
CREATE TABLE IF NOT EXISTS products (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) NOT NULL,properties JSON
);INSERT INTO products (name, properties) VALUES
('笔记本电脑', '{"brand": "Apple", "ram_gb": 16, "tags": ["MacBook Pro", "M2 Chip"]}'),
('智能手机', '{"brand": "Samsung", "ram_gb": 12, "screen_size": 6.8, "colors": ["Black", "White"]}');
B. 使用函数在SQL中动态构造JSON(进阶方式)
有时候,你需要直接在数据库层面根据其他表的数据来生成JSON。这时 JSON_OBJECT
和 JSON_ARRAY
就派上用场了。
JSON_OBJECT('key1', val1, 'key2', val2, ...)
: 根据键值对创建JSON对象。JSON_ARRAY(val1, val2, ...)
: 根据值创建JSON数组。
示例:动态插入一个键盘商品
INSERT INTO products (name, properties)
VALUES ('机械键盘',-- 使用函数动态生成JSON对象JSON_OBJECT('brand', 'Logitech','type', 'Mechanical','backlight', true,'switches', JSON_ARRAY('Red', 'Brown', 'Blue') -- 嵌套一个JSON数组)
);
这个方法在数据迁移或复杂的数据处理场景中非常有用。
3.【读取】查询与解析 JSON 数据
查询是JSON
类型最强大的部分。
A. 基础路径提取:->
与 ->>
这是最重要的查询操作符,必须理解其区别:
column->path
: 提取并返回JSON格式的值。字符串会带双引号。column->>path
: 提取并返回纯文本格式的值。字符串不带双引号,适合用于WHERE
条件判断或给应用程序返回干净的数据。
SELECTname,properties->"$.brand" AS brand_with_quotes, -- 结果: "Apple"properties->>"$.brand" AS brand_without_quotes, -- 结果: Appleproperties->"$.tags" AS tags_array -- 结果: ["MacBook Pro", "M2 Chip"]
FROM products
WHERE id = 1;
B. 在 WHERE
子句中筛选
-- 查询品牌是 Apple 的商品
SELECT name, properties FROM products WHERE properties->>"$.brand" = 'Apple';-- 查询内存大于等于 16GB 的商品 (注意类型转换)
SELECT name, properties FROM products WHERE CAST(properties->>"$.ram_gb" AS UNSIGNED) >= 16;
C. 查询数组元素
- 按索引查询:
$[0]
代表数组第一个元素。SELECT properties->>"$.tags[0]" AS first_tag FROM products WHERE id = 1; -- 结果: MacBook Pro
- 判断值是否存在于数组中:
JSON_CONTAINS()
-- 查询所有包含 "White" 颜色的手机 -- 第二个参数 '"White"' 是一个JSON字符串,所以引号是必须的 SELECT name FROM products WHERE JSON_CONTAINS(properties->'$.colors', '"White"');
D. (高级) 将JSON数组展开为行:JSON_TABLE()
从 MySQL 8.0 开始,JSON_TABLE()
是一个划时代的函数,它可以将JSON数组展开成规范的关系型数据行,便于进行聚合、关联等操作。
示例:列出所有商品的所有标签
SELECTp.name,t.tag
FROMproducts p,-- 将 properties 字段中的 tags 数组展开JSON_TABLE(p.properties,'$.tags[*]' -- [*] 表示数组中的所有元素COLUMNS (tag VARCHAR(50) PATH '$' -- 将每个数组元素的值映射到名为 tag 的列)) AS t;
结果:
name | tag |
---|---|
笔记本电脑 | MacBook Pro |
笔记本电脑 | M2 Chip |
4.【修改/删除】更新 JSON 数据
A. 更新或新增键值对:JSON_SET
, JSON_REPLACE
, JSON_INSERT
JSON_SET()
: 最常用。路径存在则更新,不存在则新增。JSON_REPLACE()
: 只更新存在的路径。JSON_INSERT()
: 只新增不存在的路径。
-- 为Apple笔记本电脑更新内存为32GB,并增加发布年份
UPDATE products
SET properties = JSON_SET(properties,'$.ram_gb', 32,'$.year', 2025
)
WHERE properties->>"$.brand" = 'Apple';
B. 删除键值对:JSON_REMOVE()
-- 删除商品属性中的 'screen_size'
UPDATE products
SET properties = JSON_REMOVE(properties, '$.screen_size')
WHERE name = '智能手机';
5. 性能优化与最佳实践
A. 为JSON字段创建索引
你不能直接索引整个JSON
列,但可以为JSON内部频繁查询的特定路径创建索引。方法是使用生成列 (Generated Column)。
示例:为 brand
字段创建索引
-- 1. 添加一个虚拟列,其值总是从 properties->>"$.brand" 计算得来
ALTER TABLE products
ADD COLUMN brand_name VARCHAR(255) AS (properties->>"$.brand") VIRTUAL;-- 2. 在这个虚拟列上创建索引
CREATE INDEX idx_products_brand_name ON products(brand_name);
之后,所有 WHERE brand_name = '...'
的查询都会自动使用该索引,速度飞快!
B. 添加检查约束(CHECK
)
为了保证JSON内部数据的某种一致性,你可以添加 CHECK
约束。
-- 确保所有商品的properties字段都必须包含 brand 和 ram_gb 这两个键
ALTER TABLE products
ADD CONSTRAINT chk_properties_keys
CHECK (JSON_CONTAINS_PATH(properties, 'all', '$.brand', '$.ram_gb')
);
6. 总结
- 写入: 可以直接插入字符串,也可以用
JSON_OBJECT
和JSON_ARRAY
在SQL中动态构造。 - 读取:
->
和->>
是基础,JSON_CONTAINS
用于数组查询,JSON_TABLE
(MySQL 8.0+) 是处理数组的强大工具。 - 修改:
JSON_SET
是最常用的更新函数。 - 性能: 必须为你频繁查询的JSON路径,通过生成列的方式来创建索引。
JSON
数据类型是关系型数据库对现代应用需求的一种强大补充,但它应该被用来补充而不是替代良好的关系型设计。