使用 MariaDB 更新 Json 对象中具有特定值的字段

Update a field with specific value inside a Json Object with MariaDB

我正在尝试更新存储在 MariaDB 的 json 列中的数据(libmysql 版本 - 5.6.43,服务器:10.3.34-MariaDB-cll-lve - MariaDB 服务器)。

我的数据结构如下:

ID json_data
1 {....}
2 {....}

其中json_data的结构如下:

{
    "company": {
        "id": "",
        "name": "",
        "address": ""
    },
    "info_company": {
          "diff_v": "1",
          "grav_v": "",
          "diff_s": "2",
          "grav_s": "",
          "diff_g": "3",
          "grav_g": "",
          "diff_ri": "4",
          "grav_ri": "2"
    }
}

我正在尝试更新 info_company 中的数据替换:

所以结果应该是:

{
    "company": {
        "id": "",
        "name": "",
        "address": ""
    },
    "info_company": {
          "diff_v": "<50%",
          "grav_v": "",
          "diff_s": "<50%",
          "grav_s": "",
          "diff_g": ">50%",
          "grav_g": "",
          "diff_ri": ">50%",
          "grav_ri": "<50%"
    }
}

通过编写此查询,我可以检索 info_company 数据,但是对于包含的每个键我无法更新新值之后的数据。

SELECT new_t.id, JSON_EXTRACT(new_t.json_data, “$.info_company“) FROM (SELECT * FROM `my_table` WHERE json_data LIKE “%info_company%”) new_t

输出:

ID json_data
1 {"diff_v": "1","grav_v": "","diff_s": "2","grav_s": "","diff_g": "3","grav_g": "","diff_ri": "4","grav_ri": "2"}

感谢您的帮助。

使用您的 json_data:

在 MariaDB 10.3.34 数据库服务器上测试
DELIMITER //
CREATE PROCEDURE percentage()
BEGIN
SELECT @info_keys:=JSON_KEYS(json_data, "$.info_company") FROM my_table;
SELECT @info_keys_num:=JSON_LENGTH(@info_keys);
WHILE @info_keys_num >= 0 DO
   SET @info_keys_num = @info_keys_num - 1;
   SELECT @info_attr:=JSON_EXTRACT(@info_keys, CONCAT("$[", @info_keys_num, "]"));
   UPDATE my_table SET json_data = JSON_REPLACE(json_data, CONCAT("$.info_company.", @info_attr), "<50%") 
      WHERE CHAR_LENGTH(JSON_VALUE(json_data, CONCAT("$.info_company.", @info_attr))) = 1 AND
            JSON_VALUE(json_data, CONCAT("$.info_company.", @info_attr)) < 3;
   UPDATE my_table SET json_data = JSON_REPLACE(json_data, CONCAT("$.info_company.", @info_attr), ">50%") 
      WHERE CHAR_LENGTH(JSON_VALUE(json_data, CONCAT("$.info_company.", @info_attr))) = 1 AND
            JSON_VALUE(json_data, CONCAT("$.info_company.", @info_attr)) > 2;
END WHILE;
END;
//
DELIMITER ;

call percentage();

输出示例:

MariaDB [test]> call percentage();
+------------------------------------------------------------------------------------+
| @info_keys:=JSON_KEYS(json_data, "$.info_company")                                 |
+------------------------------------------------------------------------------------+
| ["diff_v", "grav_v", "diff_s", "grav_s", "diff_g", "grav_g", "diff_ri", "grav_ri"] |
+------------------------------------------------------------------------------------+
1 row in set (0.001 sec)

... [cut here] ...

Query OK, 5 rows affected (0.011 sec)

您可以通过使用 CTE 生成正则表达式来匹配 info_company 中的键(和所需的匹配值)然后使用 REGEXP_REPLACE 替换 1 来解决这个问题或 2<50%34>50%:

UPDATE my_table
JOIN (
  WITH jkeys_table AS (
    SELECT id, JSON_KEYS(json_data, '$.info_company') AS jkeys
    FROM my_table
  )
  SELECT id,
         CONCAT('((?:',
                REPLACE(SUBSTRING(jkeys, 2, CHAR_LENGTH(jkeys)-2), ', ', '|'),
                ')\s*:\s*)"([12])"'
         ) AS regex12,
         CONCAT('((?:',
                REPLACE(SUBSTRING(jkeys, 2, CHAR_LENGTH(jkeys)-2), ', ', '|'),
                ')\s*:\s*)"([34])"'
         ) AS regex34
  FROM jkeys_table
) rt ON my_table.id = rt.id
SET json_data = REGEXP_REPLACE(REGEXP_REPLACE(json_data, regex12, '\1"<50%"'), regex34, '\1">50%"')

输出(对于您的示例 JSON):

id  json_data
1   {
        "company": 
        {
            "id": "",
            "name": "",
            "address": ""
        },
        "info_company": 
        {
            "diff_v": "<50%",
            "grav_v": "",
            "diff_s": "<50%",
            "grav_s": "",
            "diff_g": ">50%",
            "grav_g": "",
            "diff_ri": ">50%",
            "grav_ri": "<50%"
        }
    }

Demo on dbfiddle

如果 info_company 中的键可能存在于 json_data 中的其他地方,您需要将更改本地化到 info_company 元素。您可以通过将 UPDATESET 子句更改为:

SET json_data = JSON_REPLACE(json_data, '$.info_company',
                JSON_MERGE_PATCH(JSON_QUERY(json_data, '$.info_company'),
                                 REGEXP_REPLACE(REGEXP_REPLACE(JSON_QUERY(json_data, '$.info_company'), regex12, '\1"<50%"'), regex34, '\1">50%"')
                                )
                )

Demo on dbfiddle

如果 info_company 中的键对于每一行都相同,您可以通过仅计算一次 regex12regex34 值,然后将这些值应用于my_table 中的所有行使用 CROSS JOIN:

UPDATE my_table
CROSS JOIN (
  WITH jkeys_table AS (
    SELECT JSON_KEYS(json_data, '$.info_company') AS jkeys
    FROM my_table
    LIMIT 1
  )
  SELECT CONCAT('((?:',
                REPLACE(SUBSTRING(jkeys, 2, CHAR_LENGTH(jkeys)-2), ', ', '|'),
                ')\s*:\s*)"([12])"'
         ) AS regex12,
         CONCAT('((?:',
                REPLACE(SUBSTRING(jkeys, 2, CHAR_LENGTH(jkeys)-2), ', ', '|'),
                ')\s*:\s*)"([34])"'
         ) AS regex34
  FROM jkeys_table
) rt
SET json_data = REGEXP_REPLACE(REGEXP_REPLACE(json_data, regex12, '\1"<50%"'), regex34, '\1">50%"')

Demo on dbfiddle