假设我的 MySQL 数据库中有一个表,其中一列的类型为 JSON,并且我已将以下 JSON 保存在其中一条记录中
{
"about": "person",
"info": [
{
"fName": "John",
"lName": "Booker",
"sex": "male",
"age": 20
},
{
"fName": "Laurie",
"lName": "Sparks",
"sex": "female"
},
{
"fName": "Adam",
"lName": "Bate",
"age": 26
}
]
}
有什么方法可以提取以下内容吗?
[
{
"sex": "male",
"age": 20
},
{
"sex": "female"
},
{
"age": 26
}
]
$.info[*]
给了我这个
[
{
"fName": "John",
"lName": "Booker",
"sex": "male",
"age": 20
},
{
"fName": "Laurie",
"lName": "Sparks",
"sex": "female"
},
{
"fName": "Adam",
"lName": "Bate",
"age": 26
}
]
和$.info[*].sex
和$.info[*].age
分别给了我这些
["male", "female"]
[20, 26]
我想我也可以通过获取以下内容来工作,但我不知道如何
["male", "female", null]
[20, null, 26]
原始问题上下文
我当前使用的应用程序将一些 JSON 内容保存在 MySQL 数据库中。该 JSON 可能具有某些字段,其值是巨大的 Base64 编码图像。有时我的客户并不关心获取这些 Base64 编码图像。因此,为了提高性能,减少从数据库传输到应用程序的数据并避免在应用程序端进行处理,我想检索 JSON 数组的每个 JSON 对象元素中的所有字段,该数组不涉及存储这些巨大的图像信息。
我当前的实现是从数据库获取整个 JSON 对象,然后使用 JSON Schema 获取必要的字段(受 this 项目启发),但其性能不符合预期的 SLA。我真的想在将数据放入我的应用程序并在需要时进一步处理之前在 MySQL 服务器上进行尽可能多的数据处理。
PS:我明白;与AWS S3之类的东西不同;数据库可能不是存储大型 JSON 文档的最佳选择。但我想利用数据库的批量读取功能,这可能是其他 NoSQL 数据存储所不具备的
最佳答案
更新
从 MySQL 8.0 开始,您可以使用 JSON_TABLE
更轻松地从每个对象中提取 sex
和 age
值:
SELECT JSON_ARRAYAGG(JSON_OBJECT('sex', j.sex, 'age', j.age)) AS filtered
FROM test t
JOIN JSON_TABLE(t.j,
'$.info[*]'
COLUMNS (
sex VARCHAR(6) PATH '$.sex',
age INT PATH '$.age'
)
) j
假设列 j
中包含原始数据,则得出:
[
{"age": 20, "sex": "male"},
{"age": null, "sex": "female"},
{"age": 26, "sex": null}
]
如果您想避免 null
值,可以使用 CASE
表达式来检查它们并更改基于该值创建的对象:
SELECT JSON_ARRAYAGG(
CASE WHEN j.sex IS NULL THEN JSON_OBJECT('age', j.age)
WHEN j.age IS NULL THEN JSON_OBJECT('sex', j.sex)
ELSE JSON_OBJECT('sex', j.sex, 'age', j.age)
END
) AS filtered
FROM test t
JOIN JSON_TABLE(t.j,
'$.info[*]'
COLUMNS (
sex VARCHAR(6) PATH '$.sex',
age INT PATH '$.age'
)
) j
WHERE j.sex IS NOT NULL OR j.age IS NOT NULL
输出:
[
{"age": 20, "sex": "male"},
{"sex": "female"},
{"age": 26}
]
Demo (of both queries) on dbfiddle
原始答案
不幸的是,我认为这不能直接使用内置的 MySQL JSON 函数来完成(如果 JSON_REMOVE
可能接受通配符路径)。然而,它可以通过像这样的存储函数来实现:
CREATE FUNCTION filter_json(j JSON) RETURNS JSON
BEGIN
DECLARE n INT DEFAULT 0;
DECLARE info, sex, age, data JSON;
SET info = JSON_ARRAY();
WHILE JSON_CONTAINS_PATH(j, 'one', CONCAT('$.info[', n, ']')) DO
SET data = JSON_OBJECT();
SET sex = JSON_EXTRACT(j, CONCAT('$.info[', n, '].sex'));
IF sex IS NOT NULL THEN
SET data = JSON_INSERT(data, '$.sex', sex);
END IF;
SET age = JSON_EXTRACT(j, CONCAT('$.info[', n, '].age'));
IF age IS NOT NULL THEN
SET data = JSON_INSERT(data, '$.age', age);
END IF;
SET info = JSON_ARRAY_APPEND(info, '$', data);
SET n = n + 1;
END WHILE;
RETURN info;
END;
请注意,根据您的环境,您可能需要在输入函数之前更改分隔符(例如使用 DELIMITER//
)。
对于此查询:
SELECT filter_json('{ "about": "person", "info": [ { "fName": "John", "lName": "Booker", "sex": "male", "age": 20 }, { "fName": "Laurie", "lName": "Sparks", "sex": "female" }, { "fName": "Adam", "lName": "Bate", "age": 26 } ] }')
您将得到您想要的输出:
[
{"age": 20, "sex": "male"},
{"sex": "female"},
{"age": 26}
]
关于mysql - 从 MySQL 读取时,从 JSON 数组的每个 JSON 对象中过滤掉不需要的字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50069219/