MySQL 5.7 JSON_REMOVE 一次查询中来自数组的多个值
MySQL 5.7 JSON_REMOVE multiple values from array in one query
我有一个 MySQL 5.7 数据库,其中 JSON 列包含一个字符串数组。
我需要在一次查询中按值从数组中删除不同数量的这些字符串。
示例JSON:
["1-1-2", "1-1-3", "1-2-2", "1-2-3", "1-1-16", "1-1-17"]
我可能需要删除“1-1-16”和“1-1-17”,所以我最终得到:
["1-1-2", "1-1-3", "1-2-2", "1-2-3"]
在其他时候,我可能需要在一次查询中删除一个或多个值。
JSON_REMOVE()
可以接受多个path参数,但问题是当指定多个路径时,结果JSON_REMOVE()
在每个路径上从左到右顺序传递,这使得很难对每个传递的路径使用 JSON_SEARCH()
的结果。
例如,这不起作用,因为在删除 '1-1-16'
之后,第二个 JSON_SEARCH 将 return 成为 '1-1-17'
的不正确索引:
UPDATE json_meta
SET document =
JSON_REMOVE( document,
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-16')),
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-17')),
)
WHERE id=10
您需要这样做:
UPDATE json_meta
SET document =
JSON_REMOVE( document,
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-16')),
JSON_UNQUOTE(JSON_SEARCH(JSON_REMOVE( document,
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-16'))), 'one', '1-1-17'))
)
WHERE id=10
查询随着需要删除的每个额外字符串的增加而呈指数增长。
我想知道最好的解决方案是否只是使用链式 REPLACE()
并在每个字符串上使用逗号的所有排列(即每个字符串前面有一个逗号,后面有一个逗号,前面都有一个逗号和之后)。
最后说明:我发现另一个问题与描述的问题完全相同 here。但是,该问题没有公认的答案,而且那里的答案非常复杂。该答案表明 MySQL 5.6 没有太多 JSON 支持;我想知道,因为我使用的是 MySQL 5.7,是否有更简单的解决方案?
如果您能确保以正确的顺序搜索项目,则可以嵌套它们:
mysql> select json_remove(json_remove(document, json_unquote(json_search(document, 'one', '1-1-17'))), json_unquote(json_search(document, 'one', '1-1-16'))) as j from json_meta;
+--------------------------------------+
| j |
+--------------------------------------+
| ["1-1-2", "1-1-3", "1-2-2", "1-2-3"] |
+--------------------------------------+
但这不能在不知道顺序的情况下在单个查询中完成。
您可以在两个查询中完成,一个是获取路径,然后选择在每个嵌套级别使用哪个路径:
mysql> select json_unquote(json_search(document, 'one', '1-1-17')),
json_unquote(json_search(document, 'one', '1-1-16'))
into @path17, @path16 from json_meta;
mysql> select json_remove(json_remove(document, greatest(@path16, @path17)), least(@path16, @path17)) as j from json_meta;
+--------------------------------------+
| j |
+--------------------------------------+
| ["1-1-2", "1-1-3", "1-2-2", "1-2-3"] |
+--------------------------------------+
如果您有三个或更多的东西要删除,您必须自己对路径进行排序并以正确的顺序构建查询。
另一种解决方案是将整个文档提取到一个应用程序中,您可以在该应用程序中访问 JSON 库以将元素分解为一个数组。然后您可以消除数组元素,将数组重新编组回 JSON 并更新数据库。
如果升级到 MySQL 8.0,您可以使用 JSON_TABLE() 分解数组,过滤掉不需要的元素,然后用 JSON_ARRAYAGG().
这一切听起来越来越复杂。如果您想使用 SQL.
访问数组的单个元素或对象的字段,JSON 通常会使 SQL 查询更难,而不是更容易
演示:
mysql> select j.* from json_meta cross join json_table(document, '$[*]' columns (value varchar(10) path '$')) as j;
+--------+
| value |
+--------+
| 1-1-2 |
| 1-1-3 |
| 1-2-2 |
| 1-2-3 |
| 1-1-16 |
| 1-1-17 |
+--------+
mysql> select j.* from json_meta cross join json_table(document, '$[*]' columns (value varchar(10) path '$')) as j where value not in ('1-1-16', '1-1-17');
+-------+
| value |
+-------+
| 1-1-2 |
| 1-1-3 |
| 1-2-2 |
| 1-2-3 |
+-------+
mysql> select json_arrayagg(value) as document from json_meta cross join json_table(document, '$[*]' columns (value varchar(10) path '$')) as j where value not in ('1-1-16', '1-1-17');
+--------------------------------------+
| document |
+--------------------------------------+
| ["1-1-2", "1-1-3", "1-2-2", "1-2-3"] |
+--------------------------------------+
为了简单和高效以及易于代码开发,最好的选择是以规范化的方式存储多值属性。然后你可以这样写 SQL 查询:
DELETE FROM MyAttribute WHERE entity_id = 10 AND value IN ('1-1-16', '1-1-17');
我有一个 MySQL 5.7 数据库,其中 JSON 列包含一个字符串数组。
我需要在一次查询中按值从数组中删除不同数量的这些字符串。
示例JSON:
["1-1-2", "1-1-3", "1-2-2", "1-2-3", "1-1-16", "1-1-17"]
我可能需要删除“1-1-16”和“1-1-17”,所以我最终得到:
["1-1-2", "1-1-3", "1-2-2", "1-2-3"]
在其他时候,我可能需要在一次查询中删除一个或多个值。
JSON_REMOVE()
可以接受多个path参数,但问题是当指定多个路径时,结果JSON_REMOVE()
在每个路径上从左到右顺序传递,这使得很难对每个传递的路径使用 JSON_SEARCH()
的结果。
例如,这不起作用,因为在删除 '1-1-16'
之后,第二个 JSON_SEARCH 将 return 成为 '1-1-17'
的不正确索引:
UPDATE json_meta
SET document =
JSON_REMOVE( document,
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-16')),
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-17')),
)
WHERE id=10
您需要这样做:
UPDATE json_meta
SET document =
JSON_REMOVE( document,
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-16')),
JSON_UNQUOTE(JSON_SEARCH(JSON_REMOVE( document,
JSON_UNQUOTE(JSON_SEARCH(document, 'one', '1-1-16'))), 'one', '1-1-17'))
)
WHERE id=10
查询随着需要删除的每个额外字符串的增加而呈指数增长。
我想知道最好的解决方案是否只是使用链式 REPLACE()
并在每个字符串上使用逗号的所有排列(即每个字符串前面有一个逗号,后面有一个逗号,前面都有一个逗号和之后)。
最后说明:我发现另一个问题与描述的问题完全相同 here。但是,该问题没有公认的答案,而且那里的答案非常复杂。该答案表明 MySQL 5.6 没有太多 JSON 支持;我想知道,因为我使用的是 MySQL 5.7,是否有更简单的解决方案?
如果您能确保以正确的顺序搜索项目,则可以嵌套它们:
mysql> select json_remove(json_remove(document, json_unquote(json_search(document, 'one', '1-1-17'))), json_unquote(json_search(document, 'one', '1-1-16'))) as j from json_meta;
+--------------------------------------+
| j |
+--------------------------------------+
| ["1-1-2", "1-1-3", "1-2-2", "1-2-3"] |
+--------------------------------------+
但这不能在不知道顺序的情况下在单个查询中完成。
您可以在两个查询中完成,一个是获取路径,然后选择在每个嵌套级别使用哪个路径:
mysql> select json_unquote(json_search(document, 'one', '1-1-17')),
json_unquote(json_search(document, 'one', '1-1-16'))
into @path17, @path16 from json_meta;
mysql> select json_remove(json_remove(document, greatest(@path16, @path17)), least(@path16, @path17)) as j from json_meta;
+--------------------------------------+
| j |
+--------------------------------------+
| ["1-1-2", "1-1-3", "1-2-2", "1-2-3"] |
+--------------------------------------+
如果您有三个或更多的东西要删除,您必须自己对路径进行排序并以正确的顺序构建查询。
另一种解决方案是将整个文档提取到一个应用程序中,您可以在该应用程序中访问 JSON 库以将元素分解为一个数组。然后您可以消除数组元素,将数组重新编组回 JSON 并更新数据库。
如果升级到 MySQL 8.0,您可以使用 JSON_TABLE() 分解数组,过滤掉不需要的元素,然后用 JSON_ARRAYAGG().
这一切听起来越来越复杂。如果您想使用 SQL.
访问数组的单个元素或对象的字段,JSON 通常会使 SQL 查询更难,而不是更容易演示:
mysql> select j.* from json_meta cross join json_table(document, '$[*]' columns (value varchar(10) path '$')) as j;
+--------+
| value |
+--------+
| 1-1-2 |
| 1-1-3 |
| 1-2-2 |
| 1-2-3 |
| 1-1-16 |
| 1-1-17 |
+--------+
mysql> select j.* from json_meta cross join json_table(document, '$[*]' columns (value varchar(10) path '$')) as j where value not in ('1-1-16', '1-1-17');
+-------+
| value |
+-------+
| 1-1-2 |
| 1-1-3 |
| 1-2-2 |
| 1-2-3 |
+-------+
mysql> select json_arrayagg(value) as document from json_meta cross join json_table(document, '$[*]' columns (value varchar(10) path '$')) as j where value not in ('1-1-16', '1-1-17');
+--------------------------------------+
| document |
+--------------------------------------+
| ["1-1-2", "1-1-3", "1-2-2", "1-2-3"] |
+--------------------------------------+
为了简单和高效以及易于代码开发,最好的选择是以规范化的方式存储多值属性。然后你可以这样写 SQL 查询:
DELETE FROM MyAttribute WHERE entity_id = 10 AND value IN ('1-1-16', '1-1-17');