MySQL通过数组中的键搜索json值
Posted
技术标签:
【中文标题】MySQL通过数组中的键搜索json值【英文标题】:MySQL search json value by key in array 【发布时间】:2019-09-12 13:21:40 【问题描述】:我有一个 JSON 对象数组,并希望返回一个特定节点。为了简化我的问题,假设数组可能如下所示:
[
"Race": "Orc", "strength": 14,
"Race": "Knight", "strength": 7,
...
]
例如,我想知道骑士的力量。
函数JSON_SEARCH
,返回路径'$[1].Race'
和path operator 我可以得到力量。有没有办法将这两者结合起来,所以我可以做如下的事情?
SELECT someFunc(myCol,'$[*].Race','Orc','$.strength') AS strength
FROM myTable
我使用的是 mysql 8.0.15。
【问题讨论】:
为什么不使用普通的列和关系来代替 json,可能会更快的查找并且你不必为逻辑和魔法而烦恼。 @DanFromGermany 感谢您的编辑。我不使用普通的列和关系,因为实际数据是我不知道其结构的东西。实际数据更像是工作用品的 json 数组,我想获取电话号码。但是 json 数据可能包含普通的东西,比如手机和笔记本电脑,但也可能包含奇怪的东西,比如自行车或 nerf 枪。每种类型只能包含一次,但整个过程都在使用 json 数组,此时没有固定结构的重写是没有意义的:( 【参考方案1】:本质上,您的意思是将selection and projection 应用于 JSON 文档的数组元素和对象字段。您需要执行 WHERE 子句之类的操作来选择数组中的“行”,然后执行诸如选择其中一个字段(而不是您在选择条件中使用的字段)之类的操作。
这些是在 SQL 中使用 WHERE 子句和列的 SELECT-list 完成的,但对 JSON 执行相同操作并不是您可以使用 JSON_SEARCH() 和 JSON_CONTAINS() 之类的函数轻松完成的。
MySQL 8.0 提供的解决方案是 JSON_TABLE() 函数,用于将 JSON 文档转换为虚拟派生表——就好像您已经定义了传统的行和列一样。如果 JSON 采用您描述的格式(对象数组),它就可以工作。
这是我通过将示例数据插入到表格中所做的演示:
create table mytable ( mycol json );
insert into mytable set mycol = '["Race": "Orc", "strength": 14, "Race": "Knight", "strength": 7]';
SELECT j.* FROM mytable, JSON_TABLE(mycol,
'$[*]' COLUMNS (
race VARCHAR(10) PATH '$.Race',
strength INT PATH '$.strength'
)
) AS j;
+--------+----------+
| race | strength |
+--------+----------+
| Orc | 14 |
| Knight | 7 |
+--------+----------+
现在您可以执行通常使用 SELECT 查询执行的操作,例如选择和投影:
SELECT j.strength FROM mytable, JSON_TABLE(mycol, '$[*]'
COLUMNS (
race VARCHAR(10) PATH '$.Race',
strength INT PATH '$.strength'
)
) AS j
WHERE j.race = 'Orc'
+----------+
| strength |
+----------+
| 14 |
+----------+
这有几个问题:
您需要每次查询 JSON 数据,或者创建一个 VIEW 来执行此操作。
您说您不知道属性字段,但要编写 JSON_TABLE() 查询,您必须在查询中指定要搜索和投影的属性。您不能将其用于完全未定义的数据。
我已经回答了很多关于在 MySQL 中使用 JSON 的类似问题。我观察到,当您想要执行此类操作时,将 JSON 文档视为表格,以便您可以将 WHERE 子句中的条件应用于 JSON 数据中的字段,那么您的所有查询都会变得更加困难。然后你开始觉得你最好花几分钟来定义你的属性,这样你就可以编写更简单的查询。
【讨论】:
谢谢。我希望除了 JSON_TABLE() 之外还有另一种方法,但显然没有,除了彻底改变业务逻辑。【参考方案2】:在 SQL 5.7 中有解决此问题的方法。我将逐步编写解决方案。目标是找到骑士的力量。
我将使用与上一篇文章相同的示例表:
create table mytable ( mycol json );
insert into mytable set mycol = '["Race": "Orc", "strength": 14, "Race": "Knight", "strength": 7]';
首先,获取一个仅包含种族的数组。
select json_extract(mycol, '$[*].Race') from mytable;
+----------------------------------+
| json_extract(mycol, '$[*].Race') |
+----------------------------------+
| ["Orc", "Knight"] |
+----------------------------------+
然后,在这个数组中搜索 Knight(并取消引用)。
select json_unquote(json_search(json_extract(mycol, '$[*].Race'), 'one', 'Knight')) from mytable;
+------------------------------------------------------------------------------+
| json_unquote(json_search(json_extract(mycol, '$[*].Race'), 'one', 'Knight')) |
+------------------------------------------------------------------------------+
| $[1] |
+------------------------------------------------------------------------------+
找到索引后,从数组中获取这个元素。
select json_extract(mycol, json_unquote(json_search(json_extract(mycol, '$[*].Race'), 'one', 'Knight'))) from mytable;
+---------------------------------------------------------------------------------------------------+
| json_extract(mycol, json_unquote(json_search(json_extract(mycol, '$[*].Race'), 'one', 'Knight'))) |
+---------------------------------------------------------------------------------------------------+
| "Race": "Knight", "strength": 7 |
+---------------------------------------------------------------------------------------------------+
然后得到这个元素的强度。
select json_extract(json_extract(mycol, json_unquote(json_search(json_extract(mycol, '$[*].Race'), 'one', 'Knight'))), '$.strength') as strength from mytable;
+----------+
| strength |
+----------+
| 7 |
+----------+
您可以在其他字段上重复此操作以创建其他列。
【讨论】:
【参考方案3】:查看 JSON_CONTAINS (https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains) 并在您的 WHERE 子句中使用它来识别符合您条件的记录。
【讨论】:
请查看如何格式化您的答案以提高可读性。以上是关于MySQL通过数组中的键搜索json值的主要内容,如果未能解决你的问题,请参考以下文章
如何在JSON.NET中读取json对象值中的long值数组
如何比较字典值中的多个数组,并将每个数组元素的字典键映射到新数组/列表中