一、📥 创建 JSON 的函数
1. JSON_ARRAY() --- 创建 JSON 数组
-- 基础用法
SELECT JSON_ARRAY(1, 'hello', NULL, TRUE);
-- 结果: [1, "hello", null, true]
-- 嵌套数组
SELECT JSON_ARRAY(1, JSON_ARRAY(2, 3), 4);
-- 结果: [1, [2, 3], 4]
-- 从表中查询构建数组
SELECT JSON_ARRAY(id, name) FROM t_org WHERE id = 1;
-- 结果: [1, "测试组织"]
2. JSON_OBJECT() --- 创建 JSON 对象
-- 基础用法
SELECT JSON_OBJECT('name', '张三', 'age', 25, 'active', TRUE);
-- 结果: {"name":"张三","age":25,"active":true}
-- 嵌套对象
SELECT JSON_OBJECT(
'user', JSON_OBJECT('id', 1, 'name', '张三'),
'status', 'active'
);
-- 结果: {"user":{"id":1,"name":"张三"},"status":"active"}
-- NULL 值会被保留
SELECT JSON_OBJECT('name', '李四', 'phone', NULL);
-- 结果: {"name":"李四","phone":null}
3. JSON_QUOTE() --- 转义为 JSON 字符串
-- 给普通字符串加双引号
SELECT JSON_QUOTE('hello');
-- 结果: "hello"
-- 特殊字符会被转义
SELECT JSON_QUOTE('a/b/c');
-- 结果: "a\/b\/c"
-- 已有引号的字符串
SELECT JSON_QUOTE('He said "Hi"');
-- 结果: "He said \"Hi\""
-- 配合 JSON_CONTAINS 使用(如你之前那个SQL)
SELECT JSON_CONTAINS('["a.jpg","b.jpg"]', JSON_QUOTE('a.jpg'), '$');
-- 结果: 1
4. CAST(expr AS JSON) --- 强制转换为 JSON 类型
-- 字符串转 JSON
SELECT CAST('{"name":"张三","age":25}' AS JSON);
-- 结果: {"name":"张三","age":25}
-- 无效的 JSON 会报错
SELECT CAST('not a json' AS JSON); -- ERROR: Invalid JSON text
-- 也可以用来创建简单的 JSON 值
SELECT CAST('100' AS JSON);
-- 结果: 100 (JSON 数字类型)
二、🔍 查询 JSON 的函数
-- 从对象中提取
SELECT JSON_EXTRACT('{"name":"张三","age":25,"address":{"city":"深圳"}}', '$.name');
-- 结果: "张三"
-- 提取嵌套路径
SELECT JSON_EXTRACT('{"name":"张三","address":{"city":"深圳"}}', '$.address.city');
-- 结果: "深圳"
-- 从数组中提取(下标从0开始)
SELECT JSON_EXTRACT('["a","b","c"]', '$[1]');
-- 结果: "b"
-- 提取多个路径(返回数组)
SELECT JSON_EXTRACT('{"a":1,"b":2,"c":3}', '$.a', '$.c');
-- 结果: [1, 3]
-- 通配符:提取数组所有元素
SELECT JSON_EXTRACT('[{"id":1},{"id":2}]', '$[*].id');
-- 结果: [1, 2]
6. column->> 和 column-> --- JSON_EXTRACT 的简写
-- 测试表
CREATE TABLE test_json (id INT, info JSON);
INSERT INTO test_json VALUES (1, '{"name":"张三","age":25}');
-- -> 保留类型(数字还是数字,字符串带引号)
SELECT info->'$.name' FROM test_json; -- 结果: "张三" (带引号)
SELECT info->'$.age' FROM test_json; -- 结果: 25 (数字,不带引号)
-- ->> 去掉引号(总是返回字符串)
SELECT info->>'$.name' FROM test_json; -- 结果: 张三 (不带引号)
SELECT info->>'$.age' FROM test_json; -- 结果: 25 (字符串)
-- 用于 WHERE 条件
SELECT * FROM test_json WHERE info->>'$.name' = '张三';
7. JSON_SEARCH() --- 搜索值所在的路径
-- 在对象中搜索
SELECT JSON_SEARCH('{"a":1,"b":2,"c":2}', 'one', 2);
-- 结果: "$.b" (返回第一个匹配的路径)
-- 返回所有匹配('all')
SELECT JSON_SEARCH('{"a":1,"b":2,"c":2}', 'all', 2);
-- 结果: ["$.b", "$.c"]
-- 在数组中搜索
SELECT JSON_SEARCH('["apple","banana","apple"]', 'all', 'apple');
-- 结果: ["$[0]", "$[2]"]
-- 找不到返回 NULL
SELECT JSON_SEARCH('{"a":1}', 'one', 999);
-- 结果: NULL
-- ⚠️ 重要:JSON_SEARCH 无法搜索数组根元素的值(需要指定路径范围)
-- 如果失败,检查 JSON 结构是否匹配
8. JSON_CONTAINS() --- 判断是否包含指定值
-- 对象中查找某个值(精确匹配)
SELECT JSON_CONTAINS('{"a":1,"b":2}', '1', '$.a');
-- 结果: 1
-- 查找数组是否包含某个元素
SELECT JSON_CONTAINS('["a","b","c"]', '"b"', '$');
-- 结果: 1
-- JSON_QUOTE 可以帮你加引号
SELECT JSON_CONTAINS('["a","b","c"]', JSON_QUOTE('b'), '$');
-- 结果: 1
-- 查找对象是否包含某个 key-value 对
SELECT JSON_CONTAINS('{"user":{"name":"张三","age":25}}', '{"name":"张三"}', '$.user');
-- 结果: 1
-- 查找数组包含整个子对象
SELECT JSON_CONTAINS('[{"id":1},{"id":2}]', '{"id":2}', '$');
-- 结果: 1
9. JSON_CONTAINS_PATH() --- 判断路径是否存在
-- 检查某个 key 是否存在
SELECT JSON_CONTAINS_PATH('{"a":1,"b":2}', 'one', '$.a');
-- 结果: 1 (存在)
SELECT JSON_CONTAINS_PATH('{"a":1,"b":2}', 'one', '$.c');
-- 结果: 0 (不存在)
-- 检查多个路径是否都存在('all')
SELECT JSON_CONTAINS_PATH('{"a":1,"b":2}', 'all', '$.a', '$.b');
-- 结果: 1 (两个都存在)
-- 检查多个路径是否存在一个('one')
SELECT JSON_CONTAINS_PATH('{"a":1,"b":2}', 'one', '$.c', '$.d', '$.b');
-- 结果: 1 (至少有一个存在)
-- 检查嵌套路径
SELECT JSON_CONTAINS_PATH('{"user":{"name":"张三"}}', 'one', '$.user.name');
-- 结果: 1
10. JSON_KEYS() --- 获取所有 key
-- 获取对象的所有 key
SELECT JSON_KEYS('{"a":1,"b":2,"c":3}');
-- 结果: ["a", "b", "c"]
-- 获取嵌套对象的 key
SELECT JSON_KEYS('{"user":{"name":"张三","age":25}}', '$.user');
-- 结果: ["name", "age"]
-- 空对象返回空数组
SELECT JSON_KEYS('{}');
-- 结果: []
11. JSON_OVERLAPS() --- 判断两个 JSON 是否有交集(MySQL 8.0+)
-- 两个数组是否有公共元素
SELECT JSON_OVERLAPS('[1,2,3]', '[3,4,5]');
-- 结果: 1 (有交集,都有 3)
SELECT JSON_OVERLAPS('[1,2,3]', '[4,5,6]');
-- 结果: 0 (无交集)
-- 对象之间的比较(比较 key-value 对)
SELECT JSON_OVERLAPS('{"a":1,"b":2}', '{"b":2,"c":3}');
-- 结果: 1 (都有 b:2)
-- 数组和单个值比较
SELECT JSON_OVERLAPS('[1,2,3]', '2');
-- 结果: 1
三、✏️ 修改 JSON 的函数(返回新 JSON)
12. JSON_SET() --- 设置值(存在覆盖,不存在添加)
-- 添加新字段
SELECT JSON_SET('{"a":1}', '$.b', 2);
-- 结果: {"a":1,"b":2}
-- 覆盖已有字段
SELECT JSON_SET('{"a":1}', '$.a', 99);
-- 结果: {"a":99}
-- 添加嵌套字段
SELECT JSON_SET('{"user":{"name":"张三"}}', '$.user.age', 25);
-- 结果: {"user":{"name":"张三","age":25}}
-- 同时设置多个路径
SELECT JSON_SET('{"a":1}', '$.b', 2, '$.c', 3);
-- 结果: {"a":1,"b":2,"c":3}
-- 数组操作
SELECT JSON_SET('["a","b"]', '$[2]', 'c');
-- 结果: ["a","b","c"] (数组长度自动扩展)
13. JSON_INSERT() --- 仅当路径不存在时才插入
-- 路径不存在 → 添加
SELECT JSON_INSERT('{"a":1}', '$.b', 2);
-- 结果: {"a":1,"b":2}
-- 路径已存在 → 不覆盖(和 JSON_SET 的区别就在这里)
SELECT JSON_INSERT('{"a":1}', '$.a', 99);
-- 结果: {"a":1} (不变!)
-- 混合:已存在的不变,不存在的添加
SELECT JSON_INSERT('{"a":1,"b":2}', '$.b', 999, '$.c', 3);
-- 结果: {"a":1,"b":2,"c":3} (b 不变,c 新增)
14. JSON_REPLACE() --- 仅当路径存在时才替换
-- 路径存在 → 替换
SELECT JSON_REPLACE('{"a":1}', '$.a', 99);
-- 结果: {"a":99}
-- 路径不存在 → 不添加(和 JSON_SET 的区别就在这里)
SELECT JSON_REPLACE('{"a":1}', '$.b', 2);
-- 结果: {"a":1} (不变!)
-- 混合:存在的替换,不存在的不变
SELECT JSON_REPLACE('{"a":1,"b":2}', '$.a', 99, '$.c', 3);
-- 结果: {"a":99,"b":2} (c 没有添加)
15. JSON_REMOVE() --- 删除指定路径
-- 删除单个字段
SELECT JSON_REMOVE('{"a":1,"b":2,"c":3}', '$.b');
-- 结果: {"a":1,"c":3}
-- 删除多个字段
SELECT JSON_REMOVE('{"a":1,"b":2,"c":3}', '$.a', '$.c');
-- 结果: {"b":2}
-- 删除数组元素
SELECT JSON_REMOVE('["a","b","c"]', '$[1]');
-- 结果: ["a","c"]
-- 删除嵌套路径
SELECT JSON_REMOVE('{"user":{"name":"张三","age":25}}', '$.user.age');
-- 结果: {"user":{"name":"张三"}}
-- 删除不存在的路径 → 无变化
SELECT JSON_REMOVE('{"a":1}', '$.b');
-- 结果: {"a":1}
16. JSON_ARRAY_APPEND() --- 数组末尾追加
-- 数组追加元素
SELECT JSON_ARRAY_APPEND('[1,2,3]', '$', 4);
-- 结果: [1,2,3,4]
-- 追加字符串(需要引号)
SELECT JSON_ARRAY_APPEND('["a","b"]', '$', 'c');
-- 结果: ["a","b","c"]
-- 追加到嵌套数组
SELECT JSON_ARRAY_APPEND('{"arr":[1,2]}', '$.arr', 3);
-- 结果: {"arr":[1,2,3]}
-- 追加到多个路径
SELECT JSON_ARRAY_APPEND('[1,2]', '$', 3, '$', 4);
-- 结果: [1,2,3,4]
-- 如果目标不是数组,会自动转成数组再追加
SELECT JSON_ARRAY_APPEND('{"a":1}', '$.a', 2);
-- 结果: {"a":[1,2]} (1 被转成了数组)
17. JSON_ARRAY_INSERT() --- 数组指定位置插入
-- 在下标 1 的位置插入(下标从 0 开始)
SELECT JSON_ARRAY_INSERT('[1,2,3]', '$[1]', 99);
-- 结果: [1,99,2,3]
-- 在开头插入
SELECT JSON_ARRAY_INSERT('["a","b"]', '$[0]', 'start');
-- 结果: ["start","a","b"]
-- 在末尾插入(下标等于长度)
SELECT JSON_ARRAY_INSERT('["a","b"]', '$[2]', 'c');
-- 结果: ["a","b","c"]
-- 插入到嵌套数组
SELECT JSON_ARRAY_INSERT('{"arr":[1,3]}', '$.arr[1]', 2);
-- 结果: {"arr":[1,2,3]}
18. JSON_MERGE_PATCH() --- 合并 JSON(覆盖同名 key)
-- 合并两个对象(同名 key 被覆盖)
SELECT JSON_MERGE_PATCH('{"a":1,"b":2}', '{"b":99,"c":3}');
-- 结果: {"a":1,"b":99,"c":3}
-- 合并数组(后者替换前者,不是拼接!)
SELECT JSON_MERGE_PATCH('[1,2]', '[3,4]');
-- 结果: [3,4]
-- NULL 表示删除
SELECT JSON_MERGE_PATCH('{"a":1,"b":2}', '{"b":null}');
-- 结果: {"a":1}
-- 嵌套合并
SELECT JSON_MERGE_PATCH('{"user":{"name":"张三","age":25}}', '{"user":{"age":26}}');
-- 结果: {"user":{"name":"张三","age":26}}
19. JSON_MERGE_PRESERVE() --- 合并 JSON(保留同名 key 为数组)
-- 同名 key 合并成数组
SELECT JSON_MERGE_PRESERVE('{"a":1,"b":2}', '{"b":99,"c":3}');
-- 结果: {"a":1,"b":[2,99],"c":3}
-- 数组拼接(和 PATCH 不同)
SELECT JSON_MERGE_PRESERVE('[1,2]', '[3,4]');
-- 结果: [1,2,3,4]
-- 嵌套合并
SELECT JSON_MERGE_PRESERVE('{"user":{"name":"张三"}}', '{"user":{"age":25}}');
-- 结果: {"user":{"name":"张三","age":25}}
四、📊 JSON 信息/元数据函数
20. JSON_TYPE() --- 返回数据类型
SELECT JSON_TYPE('{"a":1}'); -- 结果: OBJECT
SELECT JSON_TYPE('[1,2,3]'); -- 结果: ARRAY
SELECT JSON_TYPE('"hello"'); -- 结果: STRING
SELECT JSON_TYPE('123'); -- 结果: INTEGER
SELECT JSON_TYPE('123.45'); -- 结果: DECIMAL
SELECT JSON_TYPE('true'); -- 结果: BOOLEAN
SELECT JSON_TYPE('null'); -- 结果: NULL
21. JSON_LENGTH() --- 返回长度
-- 对象的长度 = key 数量
SELECT JSON_LENGTH('{"a":1,"b":2,"c":3}');
-- 结果: 3
-- 数组的长度 = 元素数量
SELECT JSON_LENGTH('["a","b","c","d"]');
-- 结果: 4
-- 嵌套路径的长度
SELECT JSON_LENGTH('{"arr":[1,2,3,4,5]}', '$.arr');
-- 结果: 5
-- 空数组/空对象长度为 0
SELECT JSON_LENGTH('[]'); -- 0
SELECT JSON_LENGTH('{}'); -- 0
22. JSON_DEPTH() --- 返回最大深度
-- 标量深度为 1
SELECT JSON_DEPTH('"hello"'); -- 结果: 1
SELECT JSON_DEPTH('123'); -- 结果: 1
-- 空数组/空对象深度为 2
SELECT JSON_DEPTH('[]'); -- 结果: 2
SELECT JSON_DEPTH('{}'); -- 结果: 2
-- 简单对象深度为 2
SELECT JSON_DEPTH('{"a":1}'); -- 结果: 2
-- 嵌套对象深度为 3
SELECT JSON_DEPTH('{"a":{"b":2}}'); -- 结果: 3
-- 嵌套数组
SELECT JSON_DEPTH('[1,[2,[3]]]'); -- 结果: 4
23. JSON_VALID() --- 判断是否合法 JSON
SELECT JSON_VALID('{"name":"张三"}'); -- 结果: 1
SELECT JSON_VALID('["a","b"]'); -- 结果: 1
SELECT JSON_VALID('not a json'); -- 结果: 0
SELECT JSON_VALID('{name:"张三"}'); -- 结果: 0 (key 没有引号)
-- 数据清洗时用
SELECT * FROM t_org WHERE JSON_VALID(info) = 0; -- 找出不合法的数据
24. JSON_SCHEMA_VALID() --- Schema 校验(MySQL 8.0+)
-- 定义 Schema(要求 name 是字符串,age 是整数)
SET @schema = '{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name"]
}';
-- 校验
SELECT JSON_SCHEMA_VALID(@schema, '{"name":"张三","age":25}');
-- 结果: 1
SELECT JSON_SCHEMA_VALID(@schema, '{"name":"张三"}');
-- 结果: 1 (age 不是必须的)
SELECT JSON_SCHEMA_VALID(@schema, '{"name":123,"age":25}');
-- 结果: 0 (name 应该是字符串)
SELECT JSON_SCHEMA_VALID(@schema, '{"age":25}');
-- 结果: 0 (缺少必须的 name)
25. JSON_STORAGE_SIZE() --- 存储字节数
-- 查看 JSON 占用的存储空间
SELECT JSON_STORAGE_SIZE('{"a":1,"b":2,"c":3}');
-- 结果: 31 (字节)
-- 对比不同数据的大小
SELECT
JSON_STORAGE_SIZE('{"a":1}') AS small,
JSON_STORAGE_SIZE('{"aaaaaaaaaa":1}') AS large;
26. JSON_TABLE() --- JSON 转关系型表(MySQL 8.0+)
-- 假设有张表存储了用户的多个电话
CREATE TABLE user_phones (id INT, phones JSON);
INSERT INTO user_phones VALUES (1, '["13800138000","13800138001"]');
-- 将数组展开成多行
SELECT *
FROM user_phones,
JSON_TABLE(
phones,
'$[*]' COLUMNS(
row_id FOR ORDINALITY, -- 自动编号
phone_number VARCHAR(20) PATH '$' -- 每个元素的值
)
) AS jt;
-- 结果:
-- id | row_id | phone_number
-- 1 | 1 | 13800138000
-- 1 | 2 | 13800138001
-- 复杂示例:展开对象数组
CREATE TABLE orders (id INT, items JSON);
INSERT INTO orders VALUES (1, '[
{"product":"手机","price":5999,"qty":1},
{"product":"耳机","price":299,"qty":2}
]');
SELECT *
FROM orders,
JSON_TABLE(
items,
'$[*]' COLUMNS(
product VARCHAR(50) PATH '$.product',
price DECIMAL(10,2) PATH '$.price',
qty INT PATH '$.qty'
)
) AS jt;
-- 结果:
-- id | product | price | qty
-- 1 | 手机 | 5999.00 | 1
-- 1 | 耳机 | 299.00 | 2