简体   繁体   English

MySQL 5.7 JSON_REMOVE 在一个查询中从数组中获取多个值

[英]MySQL 5.7 JSON_REMOVE multiple values from array in one query

I have a MySQL 5.7 database with a JSON column containing an array of strings.我有一个 MySQL 5.7 数据库,其中包含一个包含字符串数组的 JSON 列。

I need to remove a varying number of those strings from the array, by value, in one query.我需要在一个查询中按值从数组中删除不同数量的这些字符串。

Example JSON:示例 JSON:

["1-1-2", "1-1-3", "1-2-2", "1-2-3", "1-1-16", "1-1-17"]

I may need to remove "1-1-16" and "1-1-17" so I end up with:我可能需要删除“1-1-16”和“1-1-17”,所以我最终得到:

["1-1-2", "1-1-3", "1-2-2", "1-2-3"]

At other times, I may need to remove just one value, or several more, in one query.在其他时候,我可能只需要在一个查询中删除一个或多个值。

JSON_REMOVE() can accept multiple path arguments, but the problem is that when multiple paths are specified, the result of JSON_REMOVE() is passed sequentially left to right on each path, which makes it very difficult to use the result of JSON_SEARCH() for each passed path. JSON_REMOVE()可以接受多个路径参数,但问题是当指定多个路径时, JSON_REMOVE()的结果在每个路径上从左到右依次传递,这使得JSON_SEARCH()的结果很难用于每一条经过的路径。

For example, this does not work, because the 2nd JSON_SEARCH will return the incorrect index for '1-1-17' after '1-1-16' has been removed:例如,这是不行的,因为第二JSON_SEARCH将返回不正确的指数'1-1-17''1-1-16'已被删除:

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

You need to do this instead:你需要这样做:

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

The query grows exponentially complex with each additional string that needs to be removed.随着需要删除的每个附加字符串,查询变得呈指数级复杂。

I'm wondering if the best solution would just be to use a chained REPLACE() with all permutations of commas on each string (ie each string with a comma before, a comma after, a comma both before and after).我想知道最好的解决方案是否只是使用链式REPLACE()和每个字符串上的所有逗号排列(即每个字符串前有逗号,后有逗号,前后有逗号)。

Final note: I found another question with the exact same issue described here .最后说明:我发现了另一个与此处描述的问题完全相同的问题。 However, that question doesn't have an accepted answer, and the one answer there is very complex.然而,这个问题没有一个公认的答案,而且唯一的答案非常复杂。 That answer indicates that MySQL 5.6 doesn't have much JSON support;这个答案表明 MySQL 5.6 没有太多的 JSON 支持; I am wondering, since I'm using MySQL 5.7, is there a simpler solution possible?我想知道,因为我使用的是 MySQL 5.7,是否有更简单的解决方案?

If you can ensure you search for the items in the right order, you can nest them:如果您可以确保以正确的顺序搜索项目,则可以嵌套它们:

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"] |
+--------------------------------------+

But this can't be done in a single query without knowing the order.但这不能在不知道顺序的情况下在单个查询中完成。

You can do it in two queries, one to get the paths and then pick which path to use at each nesting level:您可以在两个查询中执行此操作,一个是获取路径,然后在每个嵌套级别选择要使用的路径:

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"] |
+--------------------------------------+

If you had three or more things to remove, you'd have to sort the paths yourself and build the query in the right order.如果您要删除三个或更多内容,则必须自己对路径进行排序并以正确的顺序构建查询。

Another solution is to fetch the whole document into an application where you have access to a JSON library to explode the elements into an array.另一种解决方案是将整个文档提取到一个应用程序中,您可以在其中访问 JSON 库以将元素分解为数组。 Then you can eliminate array elements, re-marshal the array back into JSON and update the database.然后您可以消除数组元素,将数组重新编组回 JSON 并更新数据库。

If you upgrade to MySQL 8.0, you could use JSON_TABLE() to explode the array, filter out the elements you don't want, then implode them back into an array with JSON_ARRAYAGG().如果您升级到 MySQL 8.0,您可以使用 JSON_TABLE() 来分解数组,过滤掉不需要的元素,然后使用 JSON_ARRAYAGG() 将它们分解回数组。

This is all sounding more and more complex.这一切听起来越来越复杂。 JSON generally makes SQL queries harder, not easier, if you want to access individual elements of an array or fields of an object using SQL.如果您想使用 SQL 访问数组的单个元素或对象的字段,JSON 通常会使 SQL 查询更难,而不是更容易。

Demo:演示:

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"] |
+--------------------------------------+

The best choice for simplicity and efficiency, as well as ease of code development, is to store multi-valued attributes in a normalized manner.简单、高效以及易于代码开发的最佳选择是以规范化的方式存储多值属性。 Then you can write the SQL query this way:然后你可以这样写 SQL 查询:

DELETE FROM MyAttribute WHERE entity_id = 10 AND value IN ('1-1-16', '1-1-17');

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM