如何计算MySQL JSON数组中每个值的个数?

How to calculate count of each value in MySQL JSON array?

我有一个 MySQL table 具有以下定义:

mysql> desc person;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| id     | int(11) | NO   | PRI | NULL    |       |
| name   | text    | YES  |     | NULL    |       |
| fruits | json    | YES  |     | NULL    |       |
+--------+---------+------+-----+---------+-------+

table有一些示例数据如下:

mysql> select * from person;
+----+------+----------------------------------+
| id | name | fruits                           |
+----+------+----------------------------------+
|  1 | Tom  | ["apple", "orange"]              |
|  2 | John | ["apple", "mango"]               |
|  3 | Tony | ["apple", "mango", "strawberry"] |
+----+------+----------------------------------+

如何计算每个水果出现的总次数?例如:

+------------+-------+
| fruit      | count |    
+------------+-------+
| apple      | 3     |
| orange     | 1     |
| mango      | 2     | 
| strawberry | 1     |
+------------+-------+

一些研究表明可以使用 JSON_LENGTH 函数,但我找不到与我的场景类似的示例。

如果不先创建一个每个水果一行的 table,就无法做到这一点。

CREATE TABLE allfruits (fruit VARCHAR(10) PRIMARY KEY);
INSERT INTO allfruits VALUES ('apple'), ('orange'), ('mango'), ('strawberry');

没有从 JSON.

生成这个的好方法

获得 table 后,您可以将其加入 JSON,然后使用 GROUP BY 计算出现次数。

SELECT fruit, COUNT(*) AS count
FROM allfruits
JOIN person ON JSON_SEARCH(person.fruits, 'one', fruit) IS NOT NULL
GROUP BY fruit;

输出:

+------------+-------+
| fruit      | count |
+------------+-------+
| apple      |     3 |
| mango      |     2 |
| orange     |     1 |
| strawberry |     1 |
+------------+-------+

请注意,它会对人 table 进行 table 扫描以找到每个水果。这是相当低效的,随着你的人 table 变大,这将成为一个性能问题。

如果要针对此类查询进行优化,则不应使用 JSON 来存储水果数组。您应该以规范化的方式存储数据,表示人和水果与另一个 table 之间的多对多关系。

这与我对Is storing a delimited list in a database column really that bad?

的回答有关

您可以使用 JSON_EXTRACT() 函数提取数组所有三个分量的每个值("apple"、"mango"、"strawberry" 和 "orange") ,然后应用 UNION ALL 组合所有此类查询:

SELECT comp, count(*)
FROM
(
 SELECT JSON_EXTRACT(fruit, '$[0]') as comp FROM person UNION ALL
 SELECT JSON_EXTRACT(fruit, '$[1]') as comp FROM person UNION ALL
 SELECT JSON_EXTRACT(fruit, '$[2]') as comp FROM person 
) q
WHERE comp is not null
GROUP BY comp

确实如果你的DB版本是8,那么你也可以使用JSON_TABLE()函数:

SELECT j.fruit, count(*)
  FROM person p
  JOIN JSON_TABLE(
                 p.fruits,
                '$[*]' columns (fruit varchar(50) path '$')
       ) j
GROUP BY j.fruit;

Demo

我认为最简单的解决方案是使用 JSON_TABLE 函数。

您需要的查询是


select ft.fruit, count(ft.fruit) from person,
json_table(
  fruits,
  '$[*]' columns(
     fruit varchar(128) path '$'
    ) 
  ) as ft
  group by ft.fruit
  ;

您可以在这个 dbfiddle 中找到工作示例 Fruit demo