(目录)
本文涉及函数简介
函数 | 作用 |
---|---|
JSON_SCHEMA_VALID | 验证JSON文档是否符合模式,返回true/false |
JSON_SCHEMA_VALIDATION_REPORT | 验证JSON文档是否符合模式,返回json格式的报告 |
JSON_PRETTY | 格式化JSON文档,以标准格式返回 |
JSON_STORAGE_FREE | 返回部分更新后JSON的空闲空间 |
JSON_STORAGE_SIZE | 返回JSON文档占用的总空间 |
JSON Schema验证
从MySQL8.0.17开始,MySQL支持通过JSON Schema验证。JSON Schema需要符合Draft 4 of the JSON Schema specification MongoDB中的也存在JSON Schema验证模式,参考:MongoDB基础操作:文档操作-数据校验,Specify JSON Schema Validation
MySQL提供了两个函数来进行JSON Schema验证:
JSON_SCHEMA_VALID()
验证JSON文档是否符合模式,返回true/falseJSON_SCHEMA_VALIDATION_REPORT()
提供JSON格式的报告
对于null和无效值的处理:
- 如果任一参数为NULL,结果为NULL
- 如果任一参数不是JSON格式,返回错误
ER_INVALID_TYPE_FOR_JSON
- 如果schema不是有效的JSON,返回错误
ER_INVALID_TYPE_FOR_JSON
JSON_SCHEMA_VALID
JSON_SCHEMA_VALID(schema,document)
验证document
是否符合schema
,返回true(1)
/false(0)
示例
mysql> SET @schema = '{
'> "id": "http://json-schema.org/geo",
'> "$schema": "http://json-schema.org/draft-04/schema#",
'> "description": "A geographical coordinate",
'> "type": "object", -- 文档的类型需要是JSON对象
'> "properties": { -- 详细信息
'> "latitude": { -- latitude键的值,类型需要是数字,最大值90,最小值-90
'> "type": "number",
'> "minimum": -90,
'> "maximum": 90
'> },
'> "longitude": { -- -- longitude键的值,类型需要是数字,最大值180,最小值-180
'> "type": "number",
'> "minimum": -180,
'> "maximum": 180
'> }
'> },
'> "required": ["latitude", "longitude"] -- 文档中必须包含latitude和longitude键
'>}';
Query OK, 0 rows affected (0.01 sec)
mysql> SET @document = '{
'> "latitude": 63.444697,
'> "longitude": 10.445118
'>}';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT JSON_SCHEMA_VALID(@schema, @document);
+---------------------------------------+
| JSON_SCHEMA_VALID(@schema, @document) |
+---------------------------------------+
| 1 |
+---------------------------------------+
1 row in set (0.00 sec)
mysql> SET @document = '{}';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT JSON_SCHEMA_VALID(@schema, @document);
+---------------------------------------+
| JSON_SCHEMA_VALID(@schema, @document) |
+---------------------------------------+
| 0 |
+---------------------------------------+
1 row in set (0.00 sec)
在表中进行模式验证
可以在建表时使用CHECK
子句来设置对表数据进行模式验证,CHECK
不能引用参数,所以必须将schema
直接写在JSON_SCHEMA_VALID
函数里。
在存在模式验证的表中插入数据时,如果要插入的数据不符合模式验证要求,会报错ERROR 3819 (HY000): Check constraint 'xxx' is violated.
在MySQL8.0.19及之后的版本,可以通过SHOW WARNINGS
语句查看具体哪部分值不符合模式。
-- 创建一个带模式验证的表
mysql> CREATE TABLE geo (
-> coordinate JSON,
-> CHECK(
-> JSON_SCHEMA_VALID(
-> '{
'> "type":"object",
'> "properties":{
'> "latitude":{"type":"number", "minimum":-90, "maximum":90},
'> "longitude":{"type":"number", "minimum":-180, "maximum":180}
'> },
'> "required": ["latitude", "longitude"]
'> }',
-> coordinate
-> )
-> )
-> );
Query OK, 0 rows affected (0.45 sec)
-- 插入符合模式的值
mysql> insert into geo (coordinate) values ('{"latitude":59, "longitude":18}');
Query OK, 1 row affected (0.01 sec)
-- 插入不符合模式的值
mysql> insert into geo (coordinate) values ('{"latitude":91, "longitude":0}');
ERROR 3819 (HY000): Check constraint 'geo_chk_1' is violated.
-- 显示错误详情
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Error
Code: 3934
Message: The JSON document location '#/latitude' failed requirement 'maximum' at JSON Schema location '#/properties/latitude'.
*************************** 2. row ***************************
Level: Error
Code: 3819
Message: Check constraint 'geo_chk_1' is violated.
2 rows in set (0.00 sec)
正则表达式
在schema中可以使用正则表达式,但是如果正则表达式无效,MySQL会忽略该正则表达式,导致JSON_SCHEMA_VALID()
返回true
mysql> select JSON_SCHEMA_VALID('{"type":"string","pattern":"[0-9][3]"}','"123"'),JSON_SCHEMA_VALID('{"type":"string","pattern":"[0-9][1]"}','"123"'),JSON_SCHEMA_VALID('{"type":"string","pattern":"("}','"123"')\G
*************************** 1. row ***************************
-- 字符串类型,数字出现3次,匹配,返回1
JSON_SCHEMA_VALID('{"type":"string","pattern":"[0-9][3]"}','"123"'): 1
-- 字符串类型,数字只出现1次,不匹配,返回0
JSON_SCHEMA_VALID('{"type":"string","pattern":"[0-9][1]"}','"123"'): 0
-- (不是有效的正则表达式,结果也返回 1
JSON_SCHEMA_VALID('{"type":"string","pattern":"("}','"123"'): 1
JSON_SCHEMA_VALIDATION_REPORT
JSON_SCHEMA_VALIDATION_REPORT(schema,document)
验证document
是否符合schema
。返回一个json
格式的验证报告。
报告格式
valid
是否符合模式,true
/false
。如果结果为true,不会再返回其他属性reason
不符合模式的原因schema-location
一个URI,表示不符合模式中的哪部分位置document-location
一个URI,表示文档中的哪部分位置不符合模式schema-failed-keyword
不符合模式的关键字或属性名称
示例
mysql> SET @schema = '{
'> "id": "http://json-schema.org/geo",
'> "$schema": "http://json-schema.org/draft-04/schema#",
'> "description": "A geographical coordinate",
'> "type": "object",
'> "properties": {
'> "latitude": {
'> "type": "number",
'> "minimum": -90,
'> "maximum": 90
'> },
'> "longitude": {
'> "type": "number",
'> "minimum": -180,
'> "maximum": 180
'> }
'> },
'> "required": ["latitude", "longitude"]
'>}';
-- 符合模式的值
mysql> SET @document1 = '{
'> "latitude": 63.444697,
'> "longitude": 10.445118
'>}';
-- 不符合模式的值,longitude超过最大值
mysql> SET @document2 = '{
'> "latitude": 63.444697,
'> "longitude": 310.445118
'> }';
-- 不符合模式的值,不包含需要的属性
mysql> SET @document3 = '{}';
mysql> SELECT JSON_SCHEMA_VALIDATION_REPORT(@schema, @document1),JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document2)),JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document3))\G
*************************** 1. row ***************************
-- 符合模式,返回报告
JSON_SCHEMA_VALIDATION_REPORT(@schema, @document1): {"valid": true}
-- 不符合模式,返回报告
JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document2)): {
"valid": false, -- 验证失败
"reason": "The JSON document location '#/longitude' failed requirement 'maximum' at JSON Schema location '#/properties/longitude'", -- 失败原因
"schema-location": "#/properties/longitude", --schema位置
"document-location": "#/longitude", -- document位置
"schema-failed-keyword": "maximum" -- 导致验证失败的属性
}
-- 不符合模式,返回报告
JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document3)): {
"valid": false, --验证失败
"reason": "The JSON document location '#' failed requirement 'required' at JSON Schema location '#'", --失败
"schema-location": "#",
"document-location": "#",
"schema-failed-keyword": "required"
}
1 row in set (0.00 sec)
JSON值的部分更新
在MySQL8.0中,MySQL可以执行JSON值的部分更新,不需要删除旧文档再插入新文档,而是直接更新原始文档中的部分内容。
部分更新在以下情况下可用:
- 要更新的列数据类型是JSON
- 使用
JSON_SET()
,JSON_REPLACE()
,JSON_REMOVE()
函数来更新列值- 直接更新列值(
UPDATE mytable SET jcol = '{"a": 10, "b": 25}'
)不会进行部分更新优化 - 在一个UPDATE语句中更新多个JSON列可以进行部分更新
- 直接更新列值(
- 输入列和目标列必须是同一列。
UPDATE mytable SET jcol1 = JSON_SET(jcol2, '$.a', 100)
不能进行部分更新- 只要输入列和目标列相同,可以嵌套使用以上三个语句来进行部分更新
- 对当前数组或对象的更新,都是使用新值替换原有值,不向父数组或对象中添加新元素。
- 新值不能大于旧值
- 当一次执行多个部分更新时,如果前一次部分更新留下了空闲空间,可以忽略该条件
部分更新可以使用压缩格式写入binlog日志,以节省空间。设置binlog_row_value_options=PARTIAL_JSON
来启用
JSON_STORAGE_SIZE
JSON_STORAGE_SIZE(json_val)
计算JSON文档占用的空间大小。部分更新不会更改JSON文档占用的空间大小。
MySQL中,JSON文档的最大限制是不能超过max_allowed_packet
的值
JSON_STORAGE_FREE
JSON_STORAGE_FREE(json_val)
计算部分更新释放的空闲空间,如果部分更新的新值占用空间小于旧值,会返回一个非0值,否则返回0。
JSON_STORAGE_FREE
只能用于计算列值,如果输入一个JSON文档,总是返回0。
实际返回的是JSON列中包含的空闲空间,如果多次进行部分更新,值是累加的,在已经存在空闲空间时,如果新值占用的空间大于旧值,但是差值小于空闲空间,也可以使用已经存在的空闲空间进行部分更新。
示例
-- 建表,插入JSON数据
mysql> CREATE TABLE jtable (id int auto_increment primary key,jcol JSON);
mysql> insert into jtable (jcol) values ('{"a": 10, "b": "wxyz", "c": "[true, false]"}');
-- 查询初始空间情况
mysql> select jcol,JSON_STORAGE_SIZE(jcol),JSON_STORAGE_FREE(jcol) from jtable\G
*************************** 1. row ***************************
jcol: {"a": 10, "b": "wxyz", "c": "[true, false]"}
JSON_STORAGE_SIZE(jcol): 48
JSON_STORAGE_FREE(jcol): 0
-- 执行部分更新
mysql> UPDATE jtable SET jcol = JSON_SET(jcol, "$.a", 10, "$.b", "wxyz", "$.c", 1);
-- 查看部分更新后的空间情况,总空间不变,释放了部分空闲空间
mysql> select jcol,JSON_STORAGE_SIZE(jcol),JSON_STORAGE_FREE(jcol) from jtable\G
*************************** 1. row ***************************
jcol: {"a": 10, "b": "wxyz", "c": 1}
JSON_STORAGE_SIZE(jcol): 48
JSON_STORAGE_FREE(jcol): 14
-- 部分更新释放了一部分空间,可以在此基础上再次进行部分更新
mysql> select jcol,JSON_STORAGE_SIZE(jcol),JSON_STORAGE_FREE(jcol) from jtable\G
*************************** 1. row ***************************
jcol: {"a": 10, "b": "wxyzefg", "c": 1}
JSON_STORAGE_SIZE(jcol): 48
JSON_STORAGE_FREE(jcol): 11
-- 部分更新只对列生效,参数不存在部分更新
mysql> SET @j = '{"a": 10, "b": "wxyz", "c": "[true, false]"}';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @j,JSON_STORAGE_SIZE(@j),JSON_STORAGE_FREE(@j)\G
*************************** 1. row ***************************
@j: {"a": 10, "b": "wxyz", "c": "[true, false]"}
JSON_STORAGE_SIZE(@j): 48
JSON_STORAGE_FREE(@j): 0
mysql> SET @j = JSON_SET(@j, '$.a', 10, '$.b', 'wxyz', '$.c', '1');
mysql> SELECT @j,JSON_STORAGE_SIZE(@j),JSON_STORAGE_FREE(@j)\G
*************************** 1. row ***************************
@j: {"a": 10, "b": "wxyz", "c": "1"}
JSON_STORAGE_SIZE(@j): 36
JSON_STORAGE_FREE(@j): 0
其他函数
JSON_PRETTY
JSON_PRETTY(json_val)
将JSON文档转换成标准的,更好看的格式进行输出
标准
- 每个数组元素/字典键值对单独一行,并进行缩进
- 每个级别的缩进添加两个空格
- 用于分割元素的
,
放在每个数组元素/字典键值对的结尾 - 键值对之间使用
:
进行分割 - 空数组/空对象单独占用一行,并去除期间的空格
- 特殊符号按照
JSON_QUOTE()
的规则进行处理
示例
mysql> SELECT JSON_PRETTY('123'); # scalar
+--------------------+
| JSON_PRETTY('123') |
+--------------------+
| 123 |
+--------------------+
mysql> SELECT JSON_PRETTY("[1,3,5]"); # array
+------------------------+
| JSON_PRETTY("[1,3,5]") |
+------------------------+
| [
1,
3,
5
] |
+------------------------+
mysql> SELECT JSON_PRETTY('{"a":"10","b":"15","x":"25"}'); # object
+---------------------------------------------+
| JSON_PRETTY('{"a":"10","b":"15","x":"25"}') |
+---------------------------------------------+
| {
"a": "10",
"b": "15",
"x": "25"
} |
+---------------------------------------------+
mysql> SELECT JSON_PRETTY('["a",1,{"key1":
'> "value1"},"5", "77" ,
'> {"key2":["value3","valueX",
'> "valueY"]},"j", "2" ]')\G # nested arrays and objects
*************************** 1. row ***************************
JSON_PRETTY('["a",1,{"key1":
"value1"},"5", "77" ,
{"key2":["value3","valuex",
"valuey"]},"j", "2" ]'): [
"a",
1,
{
"key1": "value1"
},
"5",
"77",
{
"key2": [
"value3",
"valuex",
"valuey"
]
},
"j",
"2"
]