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');