7

我有一个 JSON 对象数组,并希望返回一个特定的节点。为了简化我的问题,假设数组可能如下所示:

[
    {"Race": "Orc", "strength": 14},
    {"Race": "Knight", "strength": 7},
    ...
]

例如,我想知道骑士的力量。该函数JSON_SEARCH返回路径'$[1].Race',使用路径运算符我可以获得强度。有没有办法将这两者结合起来,所以我可以做如下的事情?

SELECT someFunc(myCol,'$[*].Race','Orc','$.strength') AS strength
FROM myTable

我正在使用 MySQL 8.0.15。

4

3 回答 3

6

本质上,您的意思是将选择和投影应用于 JSON 文档的数组元素和对象字段。您需要执行 WHERE 子句之类的操作来选择数组中的“行”,然后执行诸如选择其中一个字段(而不是您在选择条件中使用的字段)之类的操作。

这些是在 SQL 中使用 WHERE 子句和列的 SELECT 列表完成的,但是对 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 |
+----------+

这有几个问题:

  1. 每次查询 JSON 数据时都需要执行此操作,或者创建一个 VIEW 来执行此操作。

  2. 您说您不知道属性字段,但要编写 JSON_TABLE() 查询,您必须在查询中指定要搜索和投影的属性。您不能将其用于完全未定义的数据。

我已经回答了很多关于在 MySQL 中使用 JSON 的类似问题。我观察到,当您想要执行此类操作时,将 JSON 文档视为表格,以便您可以将 WHERE 子句中的条件应用于 JSON 数据中的字段,那么您的所有查询都会变得更加困难。然后你开始觉得你最好花几分钟来定义你的属性,这样你就可以编写更简单的查询。

于 2019-04-23T14:12:26.907 回答
5

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

您可以在其他字段上重复此操作以创建其他列。

于 2021-01-09T08:29:01.930 回答
0

查看 JSON_CONTAINS ( https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains ) 并在 WHERE 子句中使用它来识别符合您条件的记录。

于 2019-04-23T13:22:23.663 回答