【问题标题】:Filter out unwanted fields from each JSON object of JSON array while reading from MySQL从 MySQL 读取时从 JSON 数组的每个 JSON 对象中过滤掉不需要的字段
【发布时间】:2018-10-08 16:50:25
【问题描述】:

假设我的 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 json mysql-json


    【解决方案1】:

    更新

    从 MySQL 8.0 开始,您可以更轻松地使用 JSON_TABLE 从每个对象中提取 sexage 值:

    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}
    ]
    

    Demo on dbfiddle

    【讨论】:

    • 最好指定创建函数前后的分隔符更改。
    • @rkudva 我知道你问这个问题已经有一段时间了,但如果你同时升级到 MySQL 8,我刚刚添加了一个解决方案。
    • @DylanColaco 在许多环境中是不必要的,但我已经添加了关于它的注释。
    猜你喜欢
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    相关资源
    最近更新 更多