使用 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 中的数据替换:
- “1”且“<50%”
- “2”且“<50%”
- “3”且“>50%”
- “4”,“>50%”
所以结果应该是:
{
"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%
和 3
或 4
与 >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%"
}
}
如果 info_company
中的键可能存在于 json_data
中的其他地方,您需要将更改本地化到 info_company
元素。您可以通过将 UPDATE
的 SET
子句更改为:
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%"')
)
)
如果 info_company
中的键对于每一行都相同,您可以通过仅计算一次 regex12
和 regex34
值,然后将这些值应用于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%"')
我正在尝试更新存储在 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 中的数据替换:
- “1”且“<50%”
- “2”且“<50%”
- “3”且“>50%”
- “4”,“>50%”
所以结果应该是:
{
"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
:
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%
和 3
或 4
与 >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%"
}
}
如果 info_company
中的键可能存在于 json_data
中的其他地方,您需要将更改本地化到 info_company
元素。您可以通过将 UPDATE
的 SET
子句更改为:
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%"')
)
)
如果 info_company
中的键对于每一行都相同,您可以通过仅计算一次 regex12
和 regex34
值,然后将这些值应用于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%"')