【问题标题】:How to calculate count of each value in MySQL JSON array?如何计算 MySQL JSON 数组中每个值的计数?
【发布时间】:2020-04-05 03:42:47
【问题描述】:

我有一个MySQL 表,其定义如下:

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

该表有一些示例数据如下:

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 函数,但我找不到与我的场景类似的示例。

【问题讨论】:

  • 你使用的是哪个mysql版本`?
  • @nbk MySQL 版本 8.0.18

标签: mysql sql json mysql-8.0


【解决方案1】:

您可以使用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

确实如果你的数据库版本是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_EXTRACT 函数?
  • @Arjun 不客气。事实上,因为这个问题,我建议使用 JSON_TABLE :) 我不知道也不认为 JSON_EXTRACT 是否存在动态方法。但幸运的是你有版本 8。
【解决方案2】:

我认为最简单的解决方案是使用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

【讨论】:

    【解决方案3】:

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

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

    没有从 JSON 生成它的好方法。

    获得该表后,您可以将其加入 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 |
    +------------+-------+
    

    请注意,它将在 person 表上进行表扫描以查找每个水果。这是非常低效的,并且随着您的人员表变大,这将成为性能问题。

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

    这与我对Is storing a delimited list in a database column really that bad?的回答有关

    【讨论】:

    • 感谢您的回答。我理解你关于规范化和避免 JSON 用于这种用例的观点。但是,我正在处理大多数可以很好地输入到关系数据库中的数据,但 JSON 中有一些关键字段。
    猜你喜欢
    • 1970-01-01
    • 2021-12-03
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多