【问题标题】:MySQL 5.7 JSON_REMOVE multiple values from array in one queryMySQL 5.7 JSON_REMOVE 在一个查询中从数组中删除多个值
【发布时间】:2021-07-21 21:14:00
【问题描述】:

我有一个 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()可以接受多个路径参数,但是问题是当指定多个路径时,JSON_REMOVE()的结果是从左到右依次传递的每个路径,这使得每个通过的路径都很难使用JSON_SEARCH() 的结果。

例如,这不起作用,因为在删除 '1-1-16' 后,第二个 JSON_SEARCH 将返回不正确的 '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 JSON 的又一个理由,恕我直言。如果您对表格进行规范化,这将是微不足道的。
  • 是的,MySQL 5.6 和 MySQL 5.7 和 MySQL 8.0 中的 simple 解决方案是相同的:将多值属性存储为每行一个值。然后您可以按它们的值而不是它们的位置删除两行。哪个是“第一”并不重要。
  • 您可以使用存储过程,在其中拆分一个字符串并运行一个循环并删除一个一个
  • @Barmar 我同意,但现在无法重新架构此数据库。 :)
  • 最好的办法是使用存储过程在循环中删除它们,而不是尝试在一个查询中完成。

标签: mysql mysql-5.7 mysql-json


【解决方案1】:

如果您可以确保以正确的顺序搜索项目,则可以嵌套它们:

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

【讨论】:

  • > 另一种解决方案是将整个文档提取到一个应用程序中,您可以在其中访问 JSON 库以将元素分解为一个数组。然后您可以消除数组元素,将数组重新编组回 JSON 并更新数据库。这就是我们现在所做的,在完成消除工作的同时,两次并发请求已添加到数组中。当我们“重新编组”它回来时,我们失去了那个改变..这就是我实际上试图修复的错误。但是,会接受您的回答,因为我认为它是全面而准确的。谢谢!
  • 听起来你有竞争条件。您应该使用某种集中锁定机制来确保多个客户端不在同一条记录上工作。例如,我使用 MySQL 的 GET_LOCK() 来做到这一点。
  • 没错,我遇到了竞态条件。我已经通过纯粹在 MySQL 中进行更新并为每个值进行单独查询来修复它。我相信这应该可行,因此即使有人在循环期间添加值,也不会丢失任何内容。
猜你喜欢
  • 2023-03-05
  • 1970-01-01
  • 1970-01-01
  • 2012-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-20
  • 2014-11-15
相关资源
最近更新 更多